@north-light/plugin 0.3.490 → 0.3.492

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/bundle.d.ts CHANGED
@@ -6,6 +6,9 @@ export interface BuildBundleOptions {
6
6
  readonly mountPath: string;
7
7
  /** Written into the manifest verbatim when set; omitted otherwise. */
8
8
  readonly baseUrl?: string;
9
+ /** The provider id that prefixes each declared object kind (`<providerId>.<name>`); the plugin name when omitted.
10
+ * `createFetchHandler` passes the same id it uses for scope names. */
11
+ readonly providerId?: string;
9
12
  }
10
13
  /** One file in the archive. */
11
14
  export interface BuildBundleMemoryMember {
package/dist/bundle.js CHANGED
@@ -1,8 +1,8 @@
1
- var y=Object.defineProperty;var o=(t,n)=>y(t,"name",{value:n,configurable:!0});import{PluginDefinitionError as u}from"./define.js";import{buildCommandManifest as p}from"./manifest.js";const c=512,b=100,d=new TextEncoder;async function P(t,n){const e=p(t,{...n,mountPath:n.mountPath===""?"/":n.mountPath}),r=`${JSON.stringify(e,null,2)}
2
- `,a=`${JSON.stringify({bundleVersion:1,...t.version!==void 0?{version:t.version}:{}})}
3
- `,s=w(t.memory),f=[{path:"bundle.json",contents:a},{path:"commands.json",contents:r},...s],h=E(f),l=await N(h);return{commandsJson:r,bundleJson:a,memory:s,tar:h,etag:l}}o(P,"buildBundle");function w(t){const n=new Set;return t.map(e=>{const r=g(e);if(n.has(r))throw new u(`memory document "${e.name}" produces a duplicate tar member path ${r}`);return n.add(r),{path:r,contents:$(e)}})}o(w,"buildMemoryMembers");function g(t){if(t.name.includes("\0"))throw new u(`memory document "${t.name}" must not contain a NUL character`);const n=`memory/${t.name}.md`;if(d.encode(n).length>b)throw new u(`memory document "${t.name}" produces a tar member path longer than 100 bytes (${n}); shorten its name`);return n}o(g,"memoryMemberPath");function $(t){const n=[`kind: ${t.kind}`,`when-and-why-to-read: ${x(t.whenAndWhyToRead)}`];return t.unlisted===!0&&n.push("unlisted: true"),t.rules!==void 0&&t.rules.length>0&&n.push(`surfaces: ${JSON.stringify(t.rules.map(A))}`),`---
1
+ var y=Object.defineProperty;var o=(t,n)=>y(t,"name",{value:n,configurable:!0});import{objectBlocksShapeReasons as w}from"@north-light/api/command-manifest";import{PluginDefinitionError as c}from"./define.js";import{buildCommandManifest as p}from"./manifest.js";const i=512,$=100,u=new TextEncoder;async function P(t,n){const e=p(t,{...n,mountPath:n.mountPath===""?"/":n.mountPath}),r=`${JSON.stringify(e,null,2)}
2
+ `,a=g(t,n.providerId??t.name),s=`${JSON.stringify({bundleVersion:1,...t.version!==void 0?{version:t.version}:{},...a!==void 0?{object_kinds:a}:{}})}
3
+ `,f=S(t.memory),b=[{path:"bundle.json",contents:s},{path:"commands.json",contents:r},...f],h=U(b),l=await N(h);return{commandsJson:r,bundleJson:s,memory:f,tar:h,etag:l}}o(P,"buildBundle");function g(t,n){if(t.objectKinds===void 0)return;const e=Object.fromEntries(Object.entries(t.objectKinds).map(([a,s])=>[`${n}.${a}`,s])),r=w({object_kinds:e});if(r.length>0)throw new c(r.join("; "));return e}o(g,"objectKindsBlock");function S(t){const n=new Set;return t.map(e=>{const r=j(e);if(n.has(r))throw new c(`memory document "${e.name}" produces a duplicate tar member path ${r}`);return n.add(r),{path:r,contents:k(e)}})}o(S,"buildMemoryMembers");function j(t){if(t.name.includes("\0"))throw new c(`memory document "${t.name}" must not contain a NUL character`);const n=`memory/${t.name}.md`;if(u.encode(n).length>$)throw new c(`memory document "${t.name}" produces a tar member path longer than 100 bytes (${n}); shorten its name`);return n}o(j,"memoryMemberPath");function k(t){const n=[`kind: ${t.kind}`,`when-and-why-to-read: ${x(t.whenAndWhyToRead)}`];return t.unlisted===!0&&n.push("unlisted: true"),t.rules!==void 0&&t.rules.length>0&&n.push(`surfaces: ${JSON.stringify(t.rules.map(E))}`),`---
4
4
  ${n.join(`
5
5
  `)}
6
6
  ---
7
- ${t.body}`}o($,"renderMemoryDoc");const S={boot:"boot","file-read":"read","document-read":"memory-read",command:"command","pre-command":"pre-command"};function A(t){return{on:S[t.on],at:t.deliver,...t.match!==void 0?{match:t.match}:{},...t.matchFrontmatter!==void 0?{"match-frontmatter":t.matchFrontmatter}:{},...t.if!==void 0?{gate:t.if}:{}}}o(A,"surfaceEntry");function x(t){let n='"';for(const e of t)switch(e){case"\\":n+="\\\\";break;case'"':n+='\\"';break;case`
8
- `:n+="\\n";break;case" ":n+="\\t";break;case"\r":n+="\\r";break;default:n+=e.charCodeAt(0)<=31||e==="\x7F"?`\\x${e.charCodeAt(0).toString(16).padStart(2,"0")}`:e}return`${n}"`}o(x,"yamlDoubleQuoted");function E(t){const n=[];for(const e of t){const r=d.encode(e.contents);n.push(U(e.path,r.length)),n.push(r);const a=k(r.length);a>0&&n.push(new Uint8Array(a))}return n.push(new Uint8Array(c)),n.push(new Uint8Array(c)),M(n)}o(E,"writeTar");function k(t){const n=t%c;return n===0?0:c-n}o(k,"padLength");function U(t,n){const e=new Uint8Array(c);return i(e,0,t),m(e,100,8,420),m(e,108,8,0),m(e,116,8,0),m(e,124,12,n),m(e,136,12,0),e.fill(32,148,156),e[156]=48,i(e,257,"ustar\0"),i(e,263,"00"),C(e,148,F(e)),e}o(U,"ustarHeader");function i(t,n,e){t.set(d.encode(e),n)}o(i,"writeAscii");function m(t,n,e,r){const a=e-1,s=r.toString(8).padStart(a,"0");if(s.length>a)throw new u(`tar header field at offset ${n} cannot hold value ${r} in ${a} octal digits`);i(t,n,s)}o(m,"writeOctalField");function C(t,n,e){i(t,n,e.toString(8).padStart(6,"0")),t[n+6]=0,t[n+7]=32}o(C,"writeChecksumField");function F(t){let n=0;for(const e of t)n+=e;return n}o(F,"sumBytes");function M(t){const n=t.reduce((a,s)=>a+s.length,0),e=new Uint8Array(n);let r=0;for(const a of t)e.set(a,r),r+=a.length;return e}o(M,"concatBytes");async function N(t){const n=new Uint8Array(await crypto.subtle.digest("SHA-256",t));return`"${Array.from(n,r=>r.toString(16).padStart(2,"0")).join("")}"`}o(N,"sha256Etag");export{P as buildBundle};
7
+ ${t.body}`}o(k,"renderMemoryDoc");const A={boot:"boot","file-read":"read","document-read":"memory-read",command:"command","pre-command":"pre-command"};function E(t){return{on:A[t.on],at:t.deliver,...t.match!==void 0?{match:t.match}:{},...t.matchFrontmatter!==void 0?{"match-frontmatter":t.matchFrontmatter}:{},...t.if!==void 0?{gate:t.if}:{}}}o(E,"surfaceEntry");function x(t){let n='"';for(const e of t)switch(e){case"\\":n+="\\\\";break;case'"':n+='\\"';break;case`
8
+ `:n+="\\n";break;case" ":n+="\\t";break;case"\r":n+="\\r";break;default:n+=e.charCodeAt(0)<=31||e==="\x7F"?`\\x${e.charCodeAt(0).toString(16).padStart(2,"0")}`:e}return`${n}"`}o(x,"yamlDoubleQuoted");function U(t){const n=[];for(const e of t){const r=u.encode(e.contents);n.push(O(e.path,r.length)),n.push(r);const a=B(r.length);a>0&&n.push(new Uint8Array(a))}return n.push(new Uint8Array(i)),n.push(new Uint8Array(i)),M(n)}o(U,"writeTar");function B(t){const n=t%i;return n===0?0:i-n}o(B,"padLength");function O(t,n){const e=new Uint8Array(i);return m(e,0,t),d(e,100,8,420),d(e,108,8,0),d(e,116,8,0),d(e,124,12,n),d(e,136,12,0),e.fill(32,148,156),e[156]=48,m(e,257,"ustar\0"),m(e,263,"00"),C(e,148,F(e)),e}o(O,"ustarHeader");function m(t,n,e){t.set(u.encode(e),n)}o(m,"writeAscii");function d(t,n,e,r){const a=e-1,s=r.toString(8).padStart(a,"0");if(s.length>a)throw new c(`tar header field at offset ${n} cannot hold value ${r} in ${a} octal digits`);m(t,n,s)}o(d,"writeOctalField");function C(t,n,e){m(t,n,e.toString(8).padStart(6,"0")),t[n+6]=0,t[n+7]=32}o(C,"writeChecksumField");function F(t){let n=0;for(const e of t)n+=e;return n}o(F,"sumBytes");function M(t){const n=t.reduce((a,s)=>a+s.length,0),e=new Uint8Array(n);let r=0;for(const a of t)e.set(a,r),r+=a.length;return e}o(M,"concatBytes");async function N(t){const n=new Uint8Array(await crypto.subtle.digest("SHA-256",t));return`"${Array.from(n,r=>r.toString(16).padStart(2,"0")).join("")}"`}o(N,"sha256Etag");export{P as buildBundle};
@@ -0,0 +1,18 @@
1
+ /** The calling node behind a plugin call, from the `Crtr-Caller` request header crtr sends. Each
2
+ * field is present only when the header carried it. */
3
+ export interface CrtrCaller {
4
+ /** The calling node's id. */
5
+ readonly node?: string;
6
+ /** The machine the node runs on, once its runtime is joined to an account. */
7
+ readonly machine?: string;
8
+ /** The node's profile id. */
9
+ readonly profile?: string;
10
+ /** The run the call was made in. */
11
+ readonly run?: string;
12
+ }
13
+ /**
14
+ * Parses a `Crtr-Caller` value (`node=<id>; machine=<id>; profile=<id>; run=<id>`), the inverse of
15
+ * crtr's formatter. An absent header, an empty or unknown part, and a field the header repeats
16
+ * (ambiguous, and never sent by crtr) all leave that field out rather than throw.
17
+ */
18
+ export declare function parseCrtrCaller(header: string | null | undefined): CrtrCaller;
package/dist/caller.js ADDED
@@ -0,0 +1 @@
1
+ var l=Object.defineProperty;var c=(o,e)=>l(o,"name",{value:e,configurable:!0});const s=["node","machine","profile","run"];function a(o){const e=new Map;for(const t of(o??"").split(";")){const n=t.indexOf("=");if(n<0)continue;const r=t.slice(0,n).trim();if(!s.includes(r))continue;const i=t.slice(n+1).trim();e.set(r,e.has(r)||i===""?null:i)}return Object.fromEntries(s.flatMap(t=>{const n=e.get(t);return n==null?[]:[[t,n]]}))}c(a,"parseCrtrCaller");export{a as parseCrtrCaller};
package/dist/define.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CrtrCaller } from './caller.js';
1
2
  import type { OutField, Output } from './fields.js';
2
3
  import type { LeafErrorQuestion } from './errors.js';
3
4
  import type { Receipt } from './receipts.js';
@@ -36,6 +37,12 @@ export interface LeafCaller {
36
37
  readonly requestId: string;
37
38
  /** The request's `Crtr-Run-Id`, when it carries one. */
38
39
  readonly runId?: string;
40
+ /**
41
+ * The calling node, parsed from the request's `Crtr-Caller` header; absent when the header names none. The header is
42
+ * asserted by the calling runtime and is not covered by the bearer token, so it says which of the verified `sub`'s nodes
43
+ * called; never authorize on it, authorize on `sub`.
44
+ */
45
+ readonly crtr?: CrtrCaller;
39
46
  }
40
47
  /** The second argument to every leaf handler. */
41
48
  export interface LeafContext {
@@ -149,6 +156,22 @@ export interface MemoryRule {
149
156
  * `repo`, `profile`, `depth`); the rule delivers only when it matches. */
150
157
  readonly if?: Readonly<Record<string, unknown>>;
151
158
  }
159
+ /** One event of a canvas object kind: its level (1–10) and whether a newer event of this name replaces an undelivered older one. */
160
+ export interface ObjectKindEvent {
161
+ readonly level: number;
162
+ readonly supersedes?: boolean;
163
+ }
164
+ /** A canvas object kind the plugin declares, keyed in `definePlugin({objectKinds})` by its name relative to the plugin (`repo`). */
165
+ export interface ObjectKindSpec {
166
+ /** What the kind is, for the people and agents who see it listed. */
167
+ readonly description: string;
168
+ /** The events instances of the kind publish, by event name. */
169
+ readonly events: Readonly<Record<string, ObjectKindEvent>>;
170
+ /** The leaf that renders an instance's content: a non-streaming leaf of this plugin, as its path below the plugin root. */
171
+ readonly read?: {
172
+ readonly command: readonly string[];
173
+ };
174
+ }
152
175
  /** A validated, frozen plugin, as returned by `definePlugin`. */
153
176
  export interface PluginDefinition {
154
177
  /** The top-level crtr command name, lowercase kebab-case. */
@@ -179,6 +202,8 @@ export interface PluginDefinition {
179
202
  readonly version?: number;
180
203
  /** True for a capability provider registered in the directory; every leaf then has a `group`. */
181
204
  readonly provider?: boolean;
205
+ /** Canvas object kinds by relative name; the served bundle prefixes each with the provider id. */
206
+ readonly objectKinds?: Readonly<Record<string, ObjectKindSpec>>;
182
207
  }
183
208
  /**
184
209
  * Declares one command whose handler returns a single object matching `output`.
@@ -238,6 +263,7 @@ export declare function definePlugin(spec: {
238
263
  };
239
264
  version?: number;
240
265
  provider?: boolean;
266
+ objectKinds?: Record<string, ObjectKindSpec>;
241
267
  }): PluginDefinition;
242
268
  /**
243
269
  * The lowercase kebab-case name crtr exposes for a camelCase key. Throws
package/dist/define.js CHANGED
@@ -1 +1 @@
1
- var k=Object.defineProperty;var r=(e,t)=>k(e,"name",{value:t,configurable:!0});import{mentionsFile as E,outputBaseType as l}from"@north-light/api/command-manifest";class s extends Error{static{r(this,"PluginDefinitionError")}constructor(t){super(t),this.name="PluginDefinitionError"}}function q(e){return{kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},...e.group!==void 0?{group:e.group}:{},...e.sideEffect!==void 0?{sideEffect:e.sideEffect}:{},params:e.params??{},output:e.output,effects:e.effects,streaming:!1,run:r((t,n)=>Promise.resolve(e.handler(t,n)),"run")}}r(q,"defineLeaf");function D(e){return{kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},...e.group!==void 0?{group:e.group}:{},...e.sideEffect!==void 0?{sideEffect:e.sideEffect}:{},params:e.params??{},output:e.output,effects:e.effects,streaming:!0,run:r((t,n)=>Promise.resolve(e.handler(t,n)),"run")}}r(D,"defineStreamingLeaf");function I(e){return{kind:"branch",...e}}r(I,"defineBranch");function K(e){if(R(e.name,"plugin name"),m(e.description,"plugin description"),m(e.whenToUse,"plugin whenToUse"),m(e.summary,"plugin summary"),m(e.rootEntry?.concept,"plugin rootEntry.concept"),m(e.rootEntry?.description,"plugin rootEntry.description"),m(e.rootEntry?.whenToUse,"plugin rootEntry.whenToUse"),M(e.timeouts),e.version!==void 0&&(typeof e.version!="number"||!Number.isInteger(e.version)||e.version<1))throw new s("plugin version must be an integer of at least 1");if(e.provider!==void 0&&typeof e.provider!="boolean")throw new s("plugin provider must be a boolean");if(e.provider===!0&&e.version===void 0)throw new s("a provider plugin must declare its major version");for(const[t,n]of Object.entries(e.commands))w(n,[e.name,t]);if(e.provider===!0){const t=b(e.commands,[e.name]);t!==void 0&&i(t,"needs a group because the plugin is a provider")}for(const t of e.memory??[])P(t);return Object.freeze({name:e.name,description:e.description,whenToUse:e.whenToUse,summary:e.summary,rootEntry:Object.freeze({...e.rootEntry}),commands:h(e.commands),memory:Object.freeze((e.memory??[]).map(x)),...e.timeouts!==void 0?{timeouts:Object.freeze({...e.timeouts})}:{},...e.version!==void 0?{version:e.version}:{},...e.provider!==void 0?{provider:e.provider}:{}})}r(K,"definePlugin");function b(e,t){for(const[n,f]of Object.entries(e)){const o=[...t,n];if(f.kind==="leaf"){if(f.group===void 0)return o;continue}const a=b(f.children,o);if(a!==void 0)return a}}r(b,"firstLeafWithoutGroup");function h(e){return Object.freeze(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,z(n)])))}r(h,"snapshotCommands");function z(e){return e.kind==="branch"?Object.freeze({kind:"branch",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},children:h(e.children)}):Object.freeze({kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},params:T(e.params),output:g(e.output),effects:Object.freeze([...e.effects]),streaming:e.streaming,...e.group!==void 0?{group:e.group}:{},...e.sideEffect!==void 0?{sideEffect:e.sideEffect}:{},run:e.run})}r(z,"snapshotNode");function T(e){return Object.freeze(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,A(n)])))}r(T,"snapshotParams");function A(e){return e.kind==="flag"&&e.type==="enum"?Object.freeze({...e,choices:Object.freeze([...e.choices])}):Object.freeze({...e})}r(A,"snapshotParam");function g(e){return Object.freeze(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,p(n)])))}r(g,"snapshotOutput");function p(e){return Object.freeze({...e,...e.children!==void 0?{children:g(e.children)}:{},...e.items!==void 0?{items:p(e.items)}:{},...e.values!==void 0?{values:Object.freeze([...e.values])}:{}})}r(p,"snapshotField");function w(e,t){(!u(e)||e.kind!=="branch"&&e.kind!=="leaf")&&i(t,"must be a branch or leaf defined by this package"),m(e.description,`${d(t)} description`),m(e.whenToUse,`${d(t)} whenToUse`),m(e.summary,`${d(t)} summary`),e.tier!==void 0&&e.tier!=="normal"&&e.tier!=="common"&&e.tier!=="important"&&i(t,"tier must be normal, common, or important");const n=t.at(-1);if(n===void 0&&i(t,"has no command name"),$(n,`${d(t)} command key`),e.kind==="branch"){u(e.children)||i(t,"branch children must be an object");for(const[o,a]of Object.entries(e.children))w(a,[...t,o]);return}e.group!==void 0&&(typeof e.group!="string"||!N.test(e.group))&&i(t,`group ${JSON.stringify(e.group)} must match [A-Za-z0-9_-]{1,64}`),e.sideEffect!==void 0&&typeof e.sideEffect!="boolean"&&i(t,"sideEffect must be a boolean"),u(e.params)||i(t,"leaf params must be an object");for(const[o,a]of Object.entries(e.params))$(o,`${d(t)} parameter key`),U(a,[...t,`parameter ${o}`]);Object.values(e.params).filter(o=>u(o)&&o.kind==="positional").length>1&&i(t,"a leaf may have at most one positional parameter"),u(e.output)||i(t,"leaf output must be an object");let f;for(const[o,a]of Object.entries(e.output))o.length===0&&i(t,"output field names must not be empty"),c(a,[...t,`output ${o}`],!0),l(a.type)==="file"&&(f!==void 0&&i(t,`declares file outputs ${f} and ${o}; a leaf may declare at most one file output`),f=o);(!Array.isArray(e.effects)||e.effects.length===0||!e.effects.every(o=>typeof o=="string"))&&i(t,"leaf effects must be a non-empty array of strings"),typeof e.streaming!="boolean"&&i(t,"leaf streaming must be a boolean"),typeof e.run!="function"&&i(t,"leaf handler must be a function")}r(w,"validateNode");function U(e,t){if((!u(e)||e.kind!=="flag"&&e.kind!=="positional"&&e.kind!=="stdin")&&i(t,"must be a parameter descriptor produced by param.*"),m(e.constraint,`${d(t)} constraint`),(typeof e.required!="boolean"||typeof e.repeatable!="boolean")&&i(t,"required and repeatable must be booleans"),e.kind==="stdin"){e.repeatable&&i(t,"stdin cannot be repeatable"),Object.hasOwn(e,"default")&&i(t,"stdin parameters cannot have a default");return}if(e.kind==="positional"){e.type!=="string"&&i(t,"positional parameters must have type string"),Object.hasOwn(e,"default")&&i(t,"positional parameters cannot have a default");return}if(e.type==="enum"?(!Array.isArray(e.choices)||e.choices.length===0||!e.choices.every(f=>typeof f=="string"))&&i(t,"enum parameters need at least one string choice"):e.type!=="string"&&e.type!=="int"&&e.type!=="bool"&&e.type!=="path"&&e.type!=="file"&&i(t,"flag parameters must have a supported type"),(e.type==="bool"||e.type==="path"||e.type==="file")&&e.repeatable&&i(t,`${e.type} parameters cannot be repeatable`),e.type==="path"&&e.encoding!=="text"&&e.encoding!=="base64"&&i(t,"path parameters must have text or base64 encoding"),e.type!=="path"&&e.encoding!==void 0&&i(t,"encoding is only valid on path parameters"),e.repeatable&&Object.hasOwn(e,"default")&&i(t,"repeatable parameters cannot have a default"),!Object.hasOwn(e,"default"))return;const n=e.default;e.type==="string"&&typeof n!="string"&&i(t,"string parameter defaults must be strings"),e.type==="int"&&(typeof n!="number"||!Number.isInteger(n))&&i(t,"int parameter defaults must be integers"),e.type==="bool"&&typeof n!="boolean"&&i(t,"bool parameter defaults must be booleans"),e.type==="enum"&&(typeof n!="string"||!Array.isArray(e.choices)||!e.choices.includes(n))&&i(t,"enum parameter defaults must be one of their choices"),(e.type==="path"||e.type==="file")&&i(t,`${e.type} parameters cannot have a default`)}r(U,"validateParam");function c(e,t,n){(!u(e)||typeof e.type!="string"||e.type.length===0||typeof e.constraint!="string"||typeof e.required!="boolean")&&i(t,"must be an output field descriptor produced by field.*");const f=l(e.type);if(e.kind!==void 0&&f!=="file"&&i(t,"is a parameter descriptor; declare output fields with field.*"),E(e.type)&&f!=="file"&&i(t,`has type ${e.type}; a file output is one file, declared file or file | null`),f==="file"&&!n&&i(t,"is a file, which is only valid as a top-level output field"),e.children!==void 0){f!=="object"&&i(t,"children are only valid on an object field"),u(e.children)||i(t,"children must be an object of output fields");for(const[o,a]of Object.entries(e.children))o.length===0&&i(t,"member names must not be empty"),c(a,[...t,`member ${o}`],!1)}e.items!==void 0&&(f!=="array"&&i(t,"items are only valid on an array field"),c(e.items,[...t,"items"],!1)),e.values!==void 0&&(f!=="enum"&&i(t,"values are only valid on an enum field"),(!Array.isArray(e.values)||e.values.length===0||!e.values.every(o=>typeof o=="string"&&o.length>0))&&i(t,"enum values must be a non-empty array of non-empty strings"),new Set(e.values).size!==e.values.length&&i(t,"enum values must be unique"))}r(c,"validateField");function P(e){if(!u(e)||typeof e.name!="string"||!C(e.name))throw new s("memory document name must be a safe relative path");if(e.kind!=="knowledge"&&e.kind!=="preference")throw new s(`memory document ${e.name} has an invalid kind`);if(m(e.whenAndWhyToRead,`memory document ${e.name} whenAndWhyToRead`),typeof e.body!="string")throw new s(`memory document ${e.name} body must be a string`);if(e.unlisted!==void 0&&typeof e.unlisted!="boolean")throw new s(`memory document ${e.name} unlisted must be a boolean`);if(e.rules!==void 0){if(!Array.isArray(e.rules))throw new s(`memory document ${e.name} rules must be an array`);e.rules.forEach((t,n)=>F(t,`memory document ${e.name} rules[${n}]`))}}r(P,"validateMemoryDoc");const v=["boot","file-read","document-read","command","pre-command"],j=["on","deliver","match","matchFrontmatter","if"];function F(e,t){if(!u(e))throw new s(`${t} must be an object`);for(const n of Object.keys(e))if(!j.includes(n))throw new s(`${t} has unknown key ${n}; a rule carries only ${j.join(", ")}`);if(typeof e.on!="string"||!v.includes(e.on))throw new s(`${t} on must be one of ${v.join(", ")}`);if(e.deliver!=="name"&&e.deliver!=="preview"&&e.deliver!=="content")throw new s(`${t} deliver must be name, preview or content`);if(e.match!==void 0){if(e.on==="boot")throw new s(`${t} match is meaningless on boot; drop it`);if(!Array.isArray(e.match)||e.match.length===0||!e.match.every(n=>typeof n=="string"&&n.trim().length>0))throw new s(`${t} match must be a non-empty array of non-empty globs`)}if(e.matchFrontmatter!==void 0){if(e.on!=="file-read")throw new s(`${t} matchFrontmatter applies only to file-read`);O(e.matchFrontmatter,`${t} matchFrontmatter`)}if(e.on!=="boot"&&e.match===void 0&&e.matchFrontmatter===void 0)throw new s(`${t} needs match${e.on==="file-read"?" or matchFrontmatter":""} on ${e.on}`);e.if!==void 0&&O(e.if,`${t} if`)}r(F,"validateMemoryRule");function O(e,t){if(!u(e))throw new s(`${t} must be a field\u2192matcher object`);if(!y(e,0))throw new s(`${t} must hold only strings, finite numbers, booleans, null, arrays and plain objects`)}r(O,"assertCondition");function y(e,t){return t>32?!1:e===null||typeof e=="string"||typeof e=="boolean"?!0:typeof e=="number"?Number.isFinite(e):Array.isArray(e)?e.every(n=>y(n,t+1)):!u(e)||Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null?!1:Object.values(e).every(n=>y(n,t+1))}r(y,"isPlainJson");function x(e){const{rules:t,...n}=e;return Object.freeze({...n,...t!==void 0?{rules:Object.freeze(t.map(f=>structuredClone(f)))}:{}})}r(x,"freezeMemoryDoc");function M(e){if(e!==void 0){if(!u(e))throw new s("timeouts must be an object");for(const t of["connectMs","requestMs","streamIdleMs"]){const n=e[t];if(n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0))throw new s(`timeouts.${t} must be a positive integer`)}}}r(M,"assertTimeouts");const N=/^[A-Za-z0-9_-]{1,64}$/;function $(e,t){if(_(e).replace(/-([a-z])/g,(f,o)=>o.toUpperCase())!==e)throw new s(`${t} must round-trip between camelCase and kebab-case`)}r($,"assertKey");function R(e,t){if(!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(e))throw new s(`${t} must be lowercase kebab-case`)}r(R,"assertKebabName");function _(e){const t=e.replace(/[A-Z]/g,n=>`-${n.toLowerCase()}`);if(!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(t))throw new s(`invalid key ${JSON.stringify(e)}; use camelCase keys that convert to lowercase kebab-case`);return t}r(_,"kebab");function m(e,t){if(typeof e!="string"||e.length===0)throw new s(`${t} must be a non-empty string`)}r(m,"assertText");function C(e){return e.length>0&&!e.startsWith("/")&&!e.includes("\\")&&e.split("/").every(t=>t.length>0&&t!=="."&&t!=="..")}r(C,"isSafeRelativePath");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}r(u,"isRecord");function i(e,t){throw new s(`${d(e)} ${t}`)}r(i,"fail");function d(e){return e.join(" ")}r(d,"formatPath");export{s as PluginDefinitionError,I as defineBranch,q as defineLeaf,K as definePlugin,D as defineStreamingLeaf,_ as kebab};
1
+ var T=Object.defineProperty;var r=(e,t)=>T(e,"name",{value:t,configurable:!0});import{LOCAL as P,mentionsFile as U,objectBlocksShapeReasons as K,objectKindReadReasons as R,outputBaseType as g}from"@north-light/api/command-manifest";class f extends Error{static{r(this,"PluginDefinitionError")}constructor(t){super(t),this.name="PluginDefinitionError"}}function G(e){return{kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},...e.group!==void 0?{group:e.group}:{},...e.sideEffect!==void 0?{sideEffect:e.sideEffect}:{},params:e.params??{},output:e.output,effects:e.effects,streaming:!1,run:r((t,n)=>Promise.resolve(e.handler(t,n)),"run")}}r(G,"defineLeaf");function V(e){return{kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},...e.group!==void 0?{group:e.group}:{},...e.sideEffect!==void 0?{sideEffect:e.sideEffect}:{},params:e.params??{},output:e.output,effects:e.effects,streaming:!0,run:r((t,n)=>Promise.resolve(e.handler(t,n)),"run")}}r(V,"defineStreamingLeaf");function H(e){return{kind:"branch",...e}}r(H,"defineBranch");function Q(e){if(W(e.name,"plugin name"),m(e.description,"plugin description"),m(e.whenToUse,"plugin whenToUse"),m(e.summary,"plugin summary"),m(e.rootEntry?.concept,"plugin rootEntry.concept"),m(e.rootEntry?.description,"plugin rootEntry.description"),m(e.rootEntry?.whenToUse,"plugin rootEntry.whenToUse"),I(e.timeouts),e.version!==void 0&&(typeof e.version!="number"||!Number.isInteger(e.version)||e.version<1))throw new f("plugin version must be an integer of at least 1");if(e.provider!==void 0&&typeof e.provider!="boolean")throw new f("plugin provider must be a boolean");if(e.provider===!0&&e.version===void 0)throw new f("a provider plugin must declare its major version");for(const[t,n]of Object.entries(e.commands))v(n,[e.name,t]);if(e.provider===!0){const t=w(e.commands,[e.name]);t!==void 0&&i(t,"needs a group because the plugin is a provider")}for(const t of e.memory??[])C(t);return e.objectKinds!==void 0&&x(e.objectKinds,e.name,e.commands),Object.freeze({name:e.name,description:e.description,whenToUse:e.whenToUse,summary:e.summary,rootEntry:Object.freeze({...e.rootEntry}),commands:p(e.commands),memory:Object.freeze((e.memory??[]).map(D)),...e.timeouts!==void 0?{timeouts:Object.freeze({...e.timeouts})}:{},...e.version!==void 0?{version:e.version}:{},...e.provider!==void 0?{provider:e.provider}:{},...e.objectKinds!==void 0?{objectKinds:S(e.objectKinds)}:{}})}r(Q,"definePlugin");function x(e,t,n){if(!u(e))throw new f("objectKinds must be an object keyed by relative kind name");const s={};for(const[a,d]of Object.entries(e)){if(!P.test(a))throw new f(`object kind ${JSON.stringify(a)} must be a lowercase relative name without a "."; the bundle prefixes it with the provider id`);s[`${t}.${a}`]=d}const o=K({object_kinds:s});if(o.length===0){const{leafPaths:a,streamingLeafPaths:d}=F(n);for(const[l,b]of Object.entries(s))o.push(...R(l,b,t,a,d))}if(o.length>0)throw new f(o.join("; "))}r(x,"validateObjectKinds");function F(e){const t=new Set,n=new Set,s=r((o,a)=>{for(const[d,l]of Object.entries(o)){const b=[...a,A(d)];l.kind==="branch"?s(l.children,b):(t.add(b.join(" ")),l.streaming&&n.add(b.join(" ")))}},"walk");return s(e,[]),{leafPaths:t,streamingLeafPaths:n}}r(F,"leafPathsOf");function S(e){return Object.freeze(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Object.freeze({description:n.description,events:Object.freeze(Object.fromEntries(Object.entries(n.events).map(([s,o])=>[s,Object.freeze({...o})]))),...n.read!==void 0?{read:Object.freeze({command:Object.freeze([...n.read.command])})}:{}})])))}r(S,"snapshotObjectKinds");function w(e,t){for(const[n,s]of Object.entries(e)){const o=[...t,n];if(s.kind==="leaf"){if(s.group===void 0)return o;continue}const a=w(s.children,o);if(a!==void 0)return a}}r(w,"firstLeafWithoutGroup");function p(e){return Object.freeze(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,L(n)])))}r(p,"snapshotCommands");function L(e){return e.kind==="branch"?Object.freeze({kind:"branch",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},children:p(e.children)}):Object.freeze({kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},params:M(e.params),output:j(e.output),effects:Object.freeze([...e.effects]),streaming:e.streaming,...e.group!==void 0?{group:e.group}:{},...e.sideEffect!==void 0?{sideEffect:e.sideEffect}:{},run:e.run})}r(L,"snapshotNode");function M(e){return Object.freeze(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,N(n)])))}r(M,"snapshotParams");function N(e){return e.kind==="flag"&&e.type==="enum"?Object.freeze({...e,choices:Object.freeze([...e.choices])}):Object.freeze({...e})}r(N,"snapshotParam");function j(e){return Object.freeze(Object.fromEntries(Object.entries(e).map(([t,n])=>[t,O(n)])))}r(j,"snapshotOutput");function O(e){return Object.freeze({...e,...e.children!==void 0?{children:j(e.children)}:{},...e.items!==void 0?{items:O(e.items)}:{},...e.values!==void 0?{values:Object.freeze([...e.values])}:{}})}r(O,"snapshotField");function v(e,t){(!u(e)||e.kind!=="branch"&&e.kind!=="leaf")&&i(t,"must be a branch or leaf defined by this package"),m(e.description,`${c(t)} description`),m(e.whenToUse,`${c(t)} whenToUse`),m(e.summary,`${c(t)} summary`),e.tier!==void 0&&e.tier!=="normal"&&e.tier!=="common"&&e.tier!=="important"&&i(t,"tier must be normal, common, or important");const n=t.at(-1);if(n===void 0&&i(t,"has no command name"),z(n,`${c(t)} command key`),e.kind==="branch"){u(e.children)||i(t,"branch children must be an object");for(const[o,a]of Object.entries(e.children))v(a,[...t,o]);return}e.group!==void 0&&(typeof e.group!="string"||!J.test(e.group))&&i(t,`group ${JSON.stringify(e.group)} must match [A-Za-z0-9_-]{1,64}`),e.sideEffect!==void 0&&typeof e.sideEffect!="boolean"&&i(t,"sideEffect must be a boolean"),u(e.params)||i(t,"leaf params must be an object");for(const[o,a]of Object.entries(e.params))z(o,`${c(t)} parameter key`),_(a,[...t,`parameter ${o}`]);Object.values(e.params).filter(o=>u(o)&&o.kind==="positional").length>1&&i(t,"a leaf may have at most one positional parameter"),u(e.output)||i(t,"leaf output must be an object");let s;for(const[o,a]of Object.entries(e.output))o.length===0&&i(t,"output field names must not be empty"),y(a,[...t,`output ${o}`],!0),g(a.type)==="file"&&(s!==void 0&&i(t,`declares file outputs ${s} and ${o}; a leaf may declare at most one file output`),s=o);(!Array.isArray(e.effects)||e.effects.length===0||!e.effects.every(o=>typeof o=="string"))&&i(t,"leaf effects must be a non-empty array of strings"),typeof e.streaming!="boolean"&&i(t,"leaf streaming must be a boolean"),typeof e.run!="function"&&i(t,"leaf handler must be a function")}r(v,"validateNode");function _(e,t){if((!u(e)||e.kind!=="flag"&&e.kind!=="positional"&&e.kind!=="stdin")&&i(t,"must be a parameter descriptor produced by param.*"),m(e.constraint,`${c(t)} constraint`),(typeof e.required!="boolean"||typeof e.repeatable!="boolean")&&i(t,"required and repeatable must be booleans"),e.kind==="stdin"){e.repeatable&&i(t,"stdin cannot be repeatable"),Object.hasOwn(e,"default")&&i(t,"stdin parameters cannot have a default");return}if(e.kind==="positional"){e.type!=="string"&&i(t,"positional parameters must have type string"),Object.hasOwn(e,"default")&&i(t,"positional parameters cannot have a default");return}if(e.type==="enum"?(!Array.isArray(e.choices)||e.choices.length===0||!e.choices.every(s=>typeof s=="string"))&&i(t,"enum parameters need at least one string choice"):e.type!=="string"&&e.type!=="int"&&e.type!=="bool"&&e.type!=="path"&&e.type!=="file"&&i(t,"flag parameters must have a supported type"),(e.type==="bool"||e.type==="path"||e.type==="file")&&e.repeatable&&i(t,`${e.type} parameters cannot be repeatable`),e.type==="path"&&e.encoding!=="text"&&e.encoding!=="base64"&&i(t,"path parameters must have text or base64 encoding"),e.type!=="path"&&e.encoding!==void 0&&i(t,"encoding is only valid on path parameters"),e.repeatable&&Object.hasOwn(e,"default")&&i(t,"repeatable parameters cannot have a default"),!Object.hasOwn(e,"default"))return;const n=e.default;e.type==="string"&&typeof n!="string"&&i(t,"string parameter defaults must be strings"),e.type==="int"&&(typeof n!="number"||!Number.isInteger(n))&&i(t,"int parameter defaults must be integers"),e.type==="bool"&&typeof n!="boolean"&&i(t,"bool parameter defaults must be booleans"),e.type==="enum"&&(typeof n!="string"||!Array.isArray(e.choices)||!e.choices.includes(n))&&i(t,"enum parameter defaults must be one of their choices"),(e.type==="path"||e.type==="file")&&i(t,`${e.type} parameters cannot have a default`)}r(_,"validateParam");function y(e,t,n){(!u(e)||typeof e.type!="string"||e.type.length===0||typeof e.constraint!="string"||typeof e.required!="boolean")&&i(t,"must be an output field descriptor produced by field.*");const s=g(e.type);if(e.kind!==void 0&&s!=="file"&&i(t,"is a parameter descriptor; declare output fields with field.*"),U(e.type)&&s!=="file"&&i(t,`has type ${e.type}; a file output is one file, declared file or file | null`),s==="file"&&!n&&i(t,"is a file, which is only valid as a top-level output field"),e.children!==void 0){s!=="object"&&i(t,"children are only valid on an object field"),u(e.children)||i(t,"children must be an object of output fields");for(const[o,a]of Object.entries(e.children))o.length===0&&i(t,"member names must not be empty"),y(a,[...t,`member ${o}`],!1)}e.items!==void 0&&(s!=="array"&&i(t,"items are only valid on an array field"),y(e.items,[...t,"items"],!1)),e.values!==void 0&&(s!=="enum"&&i(t,"values are only valid on an enum field"),(!Array.isArray(e.values)||e.values.length===0||!e.values.every(o=>typeof o=="string"&&o.length>0))&&i(t,"enum values must be a non-empty array of non-empty strings"),new Set(e.values).size!==e.values.length&&i(t,"enum values must be unique"))}r(y,"validateField");function C(e){if(!u(e)||typeof e.name!="string"||!B(e.name))throw new f("memory document name must be a safe relative path");if(e.kind!=="knowledge"&&e.kind!=="preference")throw new f(`memory document ${e.name} has an invalid kind`);if(m(e.whenAndWhyToRead,`memory document ${e.name} whenAndWhyToRead`),typeof e.body!="string")throw new f(`memory document ${e.name} body must be a string`);if(e.unlisted!==void 0&&typeof e.unlisted!="boolean")throw new f(`memory document ${e.name} unlisted must be a boolean`);if(e.rules!==void 0){if(!Array.isArray(e.rules))throw new f(`memory document ${e.name} rules must be an array`);e.rules.forEach((t,n)=>q(t,`memory document ${e.name} rules[${n}]`))}}r(C,"validateMemoryDoc");const $=["boot","file-read","document-read","command","pre-command"],k=["on","deliver","match","matchFrontmatter","if"];function q(e,t){if(!u(e))throw new f(`${t} must be an object`);for(const n of Object.keys(e))if(!k.includes(n))throw new f(`${t} has unknown key ${n}; a rule carries only ${k.join(", ")}`);if(typeof e.on!="string"||!$.includes(e.on))throw new f(`${t} on must be one of ${$.join(", ")}`);if(e.deliver!=="name"&&e.deliver!=="preview"&&e.deliver!=="content")throw new f(`${t} deliver must be name, preview or content`);if(e.match!==void 0){if(e.on==="boot")throw new f(`${t} match is meaningless on boot; drop it`);if(!Array.isArray(e.match)||e.match.length===0||!e.match.every(n=>typeof n=="string"&&n.trim().length>0))throw new f(`${t} match must be a non-empty array of non-empty globs`)}if(e.matchFrontmatter!==void 0){if(e.on!=="file-read")throw new f(`${t} matchFrontmatter applies only to file-read`);E(e.matchFrontmatter,`${t} matchFrontmatter`)}if(e.on!=="boot"&&e.match===void 0&&e.matchFrontmatter===void 0)throw new f(`${t} needs match${e.on==="file-read"?" or matchFrontmatter":""} on ${e.on}`);e.if!==void 0&&E(e.if,`${t} if`)}r(q,"validateMemoryRule");function E(e,t){if(!u(e))throw new f(`${t} must be a field\u2192matcher object`);if(!h(e,0))throw new f(`${t} must hold only strings, finite numbers, booleans, null, arrays and plain objects`)}r(E,"assertCondition");function h(e,t){return t>32?!1:e===null||typeof e=="string"||typeof e=="boolean"?!0:typeof e=="number"?Number.isFinite(e):Array.isArray(e)?e.every(n=>h(n,t+1)):!u(e)||Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null?!1:Object.values(e).every(n=>h(n,t+1))}r(h,"isPlainJson");function D(e){const{rules:t,...n}=e;return Object.freeze({...n,...t!==void 0?{rules:Object.freeze(t.map(s=>structuredClone(s)))}:{}})}r(D,"freezeMemoryDoc");function I(e){if(e!==void 0){if(!u(e))throw new f("timeouts must be an object");for(const t of["connectMs","requestMs","streamIdleMs"]){const n=e[t];if(n!==void 0&&(typeof n!="number"||!Number.isInteger(n)||n<=0))throw new f(`timeouts.${t} must be a positive integer`)}}}r(I,"assertTimeouts");const J=/^[A-Za-z0-9_-]{1,64}$/;function z(e,t){if(A(e).replace(/-([a-z])/g,(s,o)=>o.toUpperCase())!==e)throw new f(`${t} must round-trip between camelCase and kebab-case`)}r(z,"assertKey");function W(e,t){if(!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(e))throw new f(`${t} must be lowercase kebab-case`)}r(W,"assertKebabName");function A(e){const t=e.replace(/[A-Z]/g,n=>`-${n.toLowerCase()}`);if(!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(t))throw new f(`invalid key ${JSON.stringify(e)}; use camelCase keys that convert to lowercase kebab-case`);return t}r(A,"kebab");function m(e,t){if(typeof e!="string"||e.length===0)throw new f(`${t} must be a non-empty string`)}r(m,"assertText");function B(e){return e.length>0&&!e.startsWith("/")&&!e.includes("\\")&&e.split("/").every(t=>t.length>0&&t!=="."&&t!=="..")}r(B,"isSafeRelativePath");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}r(u,"isRecord");function i(e,t){throw new f(`${c(e)} ${t}`)}r(i,"fail");function c(e){return e.join(" ")}r(c,"formatPath");export{f as PluginDefinitionError,H as defineBranch,G as defineLeaf,Q as definePlugin,V as defineStreamingLeaf,A as kebab};
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './define.js';
4
4
  export * from './manifest.js';
5
5
  export { compareManifests, type BreakingManifestChange } from './compare.js';
6
6
  export * from './bundle.js';
7
+ export { parseCrtrCaller, type CrtrCaller } from './caller.js';
7
8
  export * from './serve.js';
8
9
  export { createNodeHandler, type NodeHandlerOptions, type NodeLikeRequest, type NodeLikeResponse } from './node.js';
9
10
  export { createCallerVerifier, type CallerVerifier, type FetchHandlerAuth } from './verify.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export*from"./params.js";export*from"./fields.js";export*from"./define.js";export*from"./manifest.js";import{compareManifests as m}from"./compare.js";export*from"./bundle.js";export*from"./serve.js";import{createNodeHandler as i}from"./node.js";import{createCallerVerifier as d}from"./verify.js";import{postReceipt as n,ReceiptPostError as E,SpendRefusedError as R}from"./receipts.js";import{LeafError as C}from"./errors.js";export{C as LeafError,E as ReceiptPostError,R as SpendRefusedError,m as compareManifests,d as createCallerVerifier,i as createNodeHandler,n as postReceipt};
1
+ export*from"./params.js";export*from"./fields.js";export*from"./define.js";export*from"./manifest.js";import{compareManifests as m}from"./compare.js";export*from"./bundle.js";import{parseCrtrCaller as s}from"./caller.js";export*from"./serve.js";import{createNodeHandler as l}from"./node.js";import{createCallerVerifier as n}from"./verify.js";import{postReceipt as E,ReceiptPostError as R,SpendRefusedError as u}from"./receipts.js";import{LeafError as L}from"./errors.js";export{L as LeafError,R as ReceiptPostError,u as SpendRefusedError,m as compareManifests,n as createCallerVerifier,l as createNodeHandler,s as parseCrtrCaller,E as postReceipt};
package/dist/serve.js CHANGED
@@ -1,3 +1,3 @@
1
- var Z=Object.defineProperty;var c=(n,e)=>Z(n,"name",{value:e,configurable:!0});import{approvalFor as K}from"./approval.js";import{requestSpendCheck as Q}from"./receipts.js";import{validateDeclaredResult as X}from"@north-light/api/command-manifest";import{errorResponse as v,LeafError as I,leafErrorResponse as P,storedAnswerHeaders as Y}from"./errors.js";import{buildCommandManifest as ee}from"./manifest.js";import{buildBundle as ne}from"./bundle.js";import{createCallerVerifier as te,normalizeProviderId as re,scopeMissing as ie}from"./verify.js";const D=/^v([1-9][0-9]*)$/;function Ae(n,e={}){if(e.token!==void 0&&e.auth!==void 0)throw new Error("createFetchHandler takes either token or auth, not both");if(e.token!==void 0&&e.receipts!==void 0)throw new Error("createFetchHandler cannot meter with a static token");if(e.receipts!==void 0&&!/^app:[A-Za-z0-9_-]{1,64}$/.test(e.receipts.provider))throw new Error("createFetchHandler receipts.provider must be an app principal (app:<id>)");const i=e.auth===void 0?void 0:re(e.auth),t=Array.isArray(n)||n.version!==void 0,r=ae(Array.isArray(n)?n:[n],Array.isArray(n),i),o=r[0].plugin.name;for(const u of r)for(const g of u.commands.values())if(g.leaf.sideEffect&&e.requests===void 0)throw new Error(`createFetchHandler needs requests for side-effect leaf ${g.path.join(" ")}`);const f=e.auth===void 0?void 0:te(e.auth);if(f!==void 0){for(const u of r)for(const g of u.commands.values())if(g.scope===void 0)throw new Error(`createFetchHandler auth needs a group on every leaf; ${g.path.join(" ")} has none`)}const d=new Map,k=e.baseUrl===void 0?void 0:J(new URL(e.baseUrl).pathname),h=r[r.length-1];e.token===void 0&&e.auth===void 0&&console.warn(`crouter plugin ${o} is serving without bearer authentication`);const p=c(async(u,g,T)=>{const s=`${g.version}\0${T}`;let l=d.get(s);if(l===void 0){const $=await ne(g.plugin,{mountPath:T,...e.baseUrl!==void 0?{baseUrl:e.baseUrl}:{}});l={tar:$.tar,etag:$.etag},d.set(s,l)}const w={ETag:l.etag};return u.headers.get("if-none-match")===l.etag?new Response(null,{status:304,headers:w}):new Response(new Uint8Array(l.tar).buffer,{headers:{...w,"Content-Type":"application/x-tar"}})},"archiveResponse");return async u=>{if(u.method!=="POST"&&e.token!==void 0&&!W(u.headers.get("authorization")??"",`Bearer ${e.token}`))return v(401,{code:"unauthorized",message:"A valid bearer token is required.",next:"Send Authorization: Bearer <token>."});const g=new URL(u.url).pathname;if(u.method==="GET"){if(!t)return p(u,h,k??J(g));const m=U(g),A=m[m.length-1],S=k??J(`/${m.slice(0,-1).join("/")}`);if(A==="versions")return Response.json({latest:h.version,supported:r.map(b=>b.version)});const j=A===void 0?null:D.exec(A),R=j===null?void 0:r.find(b=>b.version===Number(j[1]));return R===void 0?F():p(u,R,`${S}/v${R.version}`)}if(u.method!=="POST")return F();const T=performance.now();let s,l,w,$=!1,z=!1;const E=c(m=>{if(l!==void 0)try{e.onAnswer?.({...s===void 0?{}:{sub:s.sub,grantee:s.grantee,initiatingApp:s.initiatingApp,act:s.act,jti:s.jti,requestId:s.requestId,...s.runId===void 0?{}:{runId:s.runId}},...s===void 0&&y!==null&&y!==""?{requestId:y}:{},tool:l.path.slice(1).join(" "),status:m.status,durationMs:performance.now()-T,...w===void 0?{}:{receiptId:w.receipt_id},replayed:z})}catch{}return m},"answer");let y=u.headers.get("crtr-request-id");try{if(l=t?ue(r,g):ce(h.commands,g),e.token!==void 0&&!W(u.headers.get("authorization")??"",`Bearer ${e.token}`))return E(v(401,{code:"unauthorized",message:"A valid bearer token is required.",next:"Send Authorization: Bearer <token>."}));const m=f===void 0?void 0:await f(u);if(l===void 0)return F();if(m!==void 0&&!m.scope.includes(l.scope)){s=Object.freeze({...m,requestId:y??"",...u.headers.get("crtr-run-id")?{runId:u.headers.get("crtr-run-id")}:{}});const a=l.leaf.group,q=i===void 0&&a!==void 0?m.scope.find(x=>x.endsWith(`:${a}`)&&x.length>a.length+1)?.slice(0,-(a.length+1)):void 0;throw ie(l.scope,q)}if(m!==void 0){if(y===null||y==="")return E(v(400,{code:"invalid_request",message:"The request carries no Crtr-Request-Id header."}));const a=u.headers.get("crtr-run-id");s=Object.freeze({...m,requestId:y,...a!==null&&a!==""?{runId:a}:{}})}const A=await fe(u,l.manifest,m!==void 0);if(l.leaf.sideEffect){if(y===null||y==="")return E(v(400,{code:"invalid_request",message:"The request carries no Crtr-Request-Id header."}));const a=s===void 0?null:{sub:s.sub,grantee:s.grantee};if(!await e.requests.record(y,a)){z=!0;const x=await de(e.requests,y,a,l.plugin.timeouts?.requestMs??3e4);if(x!==!0){const B=x.headers.get("Crtr-Receipt");if(B!==null)try{w=JSON.parse(B)}catch{}return E(x)}z=!1}$=!0}const S=l;let j;const R={request:u,...s!==void 0?{caller:s}:{},approval:K(u),meter(a){if(s===void 0)throw new Error("ctx.meter needs an authenticated caller");if(w!==void 0)throw new Error("ctx.meter may be called only once");if(e.receipts===void 0)throw new Error("ctx.meter needs receipts with a provider and outbox");const q=S.path.slice(1).join(" "),x=a.usdEquivalent??(j?.mode==="unit"&&j.usd_per_unit!==null?a.units*j.usd_per_unit:0);return w={receipt_id:s.requestId,provider:e.receipts.provider,tool:q,sub:s.sub,grantee:s.grantee,payer:s.payer,units:a.units,unit:a.unit,usd_equivalent:x,request_id:s.requestId,...s.runId!==void 0?{run_id:s.runId}:{},time:new Date().toISOString()},w},async checkSpend(){if(s===void 0)throw new Error("ctx.checkSpend needs an authenticated caller");if(e.receipts===void 0)throw new Error("ctx.checkSpend needs receipts with a provider and outbox");if(e.receipts.ledger===void 0)throw new Error("ctx.checkSpend needs receipts.ledger (the provider's directory client credentials)");const a=/^Bearer ([^\s]+)$/.exec(u.headers.get("authorization")??"")?.[1];if(a===void 0)throw new Error("ctx.checkSpend needs the request's bearer token");j=(await Q(e.auth.issuer,a,S.path.slice(1).join(" "),e.receipts.ledger)).price}};let b,H;try{const a=await l.leaf.run(A,R);b=l.leaf.streaming?await pe(a,e.onError,l.path):me(a,l.manifest)}catch(a){a instanceof I?(b=P(a),H=a.headers):(O(e.onError,a,l.path),b=C(a))}if($&&b.status===403&&await oe(b))return await e.requests.delete(y),E(G(b,w));try{if($)await e.requests.store(y,{status:b.status,body:await b.clone().text(),...H!==void 0?{headers:{...H}}:{},...w!==void 0?{receipt:w}:{}});else if(w!==void 0){if(e.receipts===void 0)throw new Error(`No receipt outbox for ${S.path.join(" ")}`);await e.receipts.outbox.save(w)}}catch(a){return O(e.onError,a,l.path,w),E(C(a))}return E(G(b,w))}catch(m){return m instanceof I?E(P(m)):(O(e.onError,m,l?.path??[]),E(C(m)))}}}c(Ae,"createFetchHandler");function C(n){return v(500,{code:"handler_failed",message:n instanceof Error?n.message:String(n)})}c(C,"handlerFailed");function G(n,e){if(e===void 0)return n;const i=new Headers(n.headers);return i.set("Crtr-Receipt",JSON.stringify(e)),new Response(n.body,{status:n.status,headers:i})}c(G,"responseWithReceipt");async function oe(n){try{const e=await n.clone().json();return M(e)&&M(e.error)&&e.error.code==="approval_required"}catch{return!1}}c(oe,"isApprovalRequired");async function de(n,e,i,t){for(;;){const r=await n.read(e);if(r===null){if(await n.record(e,i))return!0;continue}if(r.identity?.sub!==i?.sub||r.identity?.grantee!==i?.grantee)return v(403,{code:"provider_refused",message:"This request id belongs to a different caller."});if(r.answer!==null){const o=Y(r.answer.headers);return o.set("Content-Type","application/json"),r.answer.receipt!==void 0&&o.set("Crtr-Receipt",JSON.stringify(r.answer.receipt)),new Response(r.answer.body,{status:r.answer.status,headers:o})}if(Date.now()>=new Date(r.recordedAt).getTime()+t)return C(new Error("The earlier request has no stored answer."));await new Promise(o=>setTimeout(o,Math.min(25,Math.max(1,new Date(r.recordedAt).getTime()+t-Date.now()))))}}c(de,"replay");function ae(n,e,i){if(n.length===0)throw new Error("createFetchHandler needs at least one plugin definition");const t=n[0].name,r=new Map;for(const o of n){if(o.name!==t)throw new Error(`createFetchHandler serves one plugin; got ${t} and ${o.name}`);if(e&&o.version===void 0)throw new Error(`createFetchHandler needs an explicit version on every definition in an array; ${t} has none`);const f=o.version??1;if(r.has(f))throw new Error(`createFetchHandler got two definitions of ${t} major ${f}`);r.set(f,{version:f,plugin:o,commands:se(o,i)})}return[...r.values()].sort((o,f)=>o.version-f.version)}c(ae,"servedMajors");function se(n,e){const t=ee(n,{mountPath:""}).mounts[0]?.node;if(t===void 0||t.kind!=="branch")throw new Error("Generated plugin manifest has no root branch");const r=new Map;return L(t.children,n.commands,[t.name],n,r,e),r}c(se,"commandMap");function L(n,e,i,t,r,o){const f=Object.entries(e);if(n.length!==f.length)throw new Error("Generated plugin manifest does not match the command definition");for(const[d,[k,h]]of f.entries()){const p=n[d];if(p===void 0||p.kind!==h.kind)throw new Error(`Generated plugin manifest does not match command ${k}`);const u=[...i,p.name];if(p.kind==="branch"&&h.kind==="branch"){L(p.children,h.children,u,t,r,o);continue}if(p.kind==="leaf"&&h.kind==="leaf"){r.set(u.join("/"),{path:u,leaf:h,plugin:t,manifest:p,...h.group!==void 0?{scope:`${o??t.name}:${h.group}`}:{}});continue}throw new Error(`Generated plugin manifest does not match command ${k}`)}}c(L,"collectCommands");function ce(n,e){const i=U(e);for(let t=0;t<i.length;t+=1){const r=n.get(i.slice(t).join("/"));if(r!==void 0)return r}}c(ce,"findCommand");function ue(n,e){const i=U(e);for(let t=i.length-2;t>=0;t-=1){const r=D.exec(i[t]);if(r===null)continue;const f=n.find(d=>d.version===Number(r[1]))?.commands.get(i.slice(t+1).join("/"));if(f!==void 0)return f}}c(ue,"findVersionedCommand");function U(n){return n.split("/").filter(e=>e.length>0)}c(U,"pathSegments");async function fe(n,e,i){const t=n.body===null?"":await n.text();let r;if(t.length===0)r={};else try{const d=JSON.parse(t);if(!M(d))throw new Error("not an object");r=d}catch{throw N("Request body must be a JSON object.",i?"body":void 0,i,t)}const o=new Map(e.params.map(d=>[d.name,d]));for(const d of Object.keys(r))if(!o.has(d))throw i?N(`Unknown parameter ${d}.`,d,!0,r[d]):new I({code:"unknown_parameter",message:`Unknown parameter ${d}.`,field:d});const f={};for(const d of e.params){if(!Object.hasOwn(r,d.name)){if(d.required)throw N(`Missing required parameter ${d.name}.`,d.name,i,null);continue}const k=r[d.name],h=le(k,d);if(h===void 0)throw N(`Invalid value for parameter ${d.name}.`,d.name,i,k);f[we(d.name)]=h}return f}c(fe,"decodeInput");function le(n,e){if(e.kind!=="stdin"&&e.kind!=="context-file"&&e.repeatable){if(!Array.isArray(n))return;const i=n.map(t=>V(t,e));return i.some(t=>t===void 0)?void 0:i}return V(n,e)}c(le,"decodeParameter");function V(n,e){if(e.kind==="stdin"||e.kind==="positional")return typeof n=="string"?n:void 0;if(e.kind!=="context-file"){if(e.type==="string")return typeof n=="string"?n:void 0;if(e.type==="int")return typeof n=="number"&&Number.isInteger(n)?n:void 0;if(e.type==="bool")return typeof n=="boolean"?n:void 0;if(e.type==="enum")return typeof n=="string"&&Array.isArray(e.choices)&&e.choices.includes(n)?n:void 0;if(e.type==="file")return typeof n=="string"?n:void 0;if(e.type==="path")return typeof n!="string"?void 0:e.encoding==="base64"?he(n):n}}c(V,"decodeScalar");function he(n){if(!(!/^[A-Za-z0-9+/]*={0,2}$/.test(n)||n.length%4!==0))try{const e=atob(n);return Uint8Array.from(e,i=>i.charCodeAt(0))}catch{return}}c(he,"decodeBase64");function me(n,e){let i,t;try{i=JSON.stringify(n),t=JSON.parse(i)}catch{throw _("result")}if(!M(t))throw _("result");const r=X(e.output,t);if(r!==null)throw _(r.field,r.receivedType);for(const o of Object.keys(t))if(!e.output.some(f=>f.name===o))throw _(o);return new Response(i,{headers:{"Content-Type":"application/json"}})}c(me,"resultResponse");async function pe(n,e,i){if(!ge(n))throw _("result");const t=n[Symbol.asyncIterator](),r=new TextEncoder,o=await t.next();let f=o.done,d=f?void 0:r.encode(`${JSON.stringify(o.value)}
1
+ var Z=Object.defineProperty;var c=(n,e)=>Z(n,"name",{value:e,configurable:!0});import{approvalFor as K}from"./approval.js";import{requestSpendCheck as Q}from"./receipts.js";import{validateDeclaredResult as X}from"@north-light/api/command-manifest";import{errorResponse as $,LeafError as _,leafErrorResponse as P,storedAnswerHeaders as Y}from"./errors.js";import{buildCommandManifest as ee}from"./manifest.js";import{buildBundle as ne}from"./bundle.js";import{parseCrtrCaller as te}from"./caller.js";import{createCallerVerifier as re,normalizeProviderId as ie,scopeMissing as oe}from"./verify.js";const D=/^v([1-9][0-9]*)$/;function Re(n,e={}){if(e.token!==void 0&&e.auth!==void 0)throw new Error("createFetchHandler takes either token or auth, not both");if(e.token!==void 0&&e.receipts!==void 0)throw new Error("createFetchHandler cannot meter with a static token");if(e.receipts!==void 0&&!/^app:[A-Za-z0-9_-]{1,64}$/.test(e.receipts.provider))throw new Error("createFetchHandler receipts.provider must be an app principal (app:<id>)");const i=e.auth===void 0?void 0:ie(e.auth),t=Array.isArray(n)||n.version!==void 0,r=se(Array.isArray(n)?n:[n],Array.isArray(n),i),o=r[0].plugin.name;for(const u of r)for(const g of u.commands.values())if(g.leaf.sideEffect&&e.requests===void 0)throw new Error(`createFetchHandler needs requests for side-effect leaf ${g.path.join(" ")}`);const f=e.auth===void 0?void 0:re(e.auth);if(f!==void 0){for(const u of r)for(const g of u.commands.values())if(g.scope===void 0)throw new Error(`createFetchHandler auth needs a group on every leaf; ${g.path.join(" ")} has none`)}const d=new Map,k=e.baseUrl===void 0?void 0:J(new URL(e.baseUrl).pathname),h=r[r.length-1];e.token===void 0&&e.auth===void 0&&console.warn(`crouter plugin ${o} is serving without bearer authentication`);const p=c(async(u,g,O)=>{const s=`${g.version}\0${O}`;let l=d.get(s);if(l===void 0){const A=await ne(g.plugin,{mountPath:O,...i!==void 0?{providerId:i}:{},...e.baseUrl!==void 0?{baseUrl:e.baseUrl}:{}});l={tar:A.tar,etag:A.etag},d.set(s,l)}const w={ETag:l.etag};return u.headers.get("if-none-match")===l.etag?new Response(null,{status:304,headers:w}):new Response(new Uint8Array(l.tar).buffer,{headers:{...w,"Content-Type":"application/x-tar"}})},"archiveResponse");return async u=>{if(u.method!=="POST"&&e.token!==void 0&&!W(u.headers.get("authorization")??"",`Bearer ${e.token}`))return $(401,{code:"unauthorized",message:"A valid bearer token is required.",next:"Send Authorization: Bearer <token>."});const g=new URL(u.url).pathname;if(u.method==="GET"){if(!t)return p(u,h,k??J(g));const m=U(g),S=m[m.length-1],R=k??J(`/${m.slice(0,-1).join("/")}`);if(S==="versions")return Response.json({latest:h.version,supported:r.map(b=>b.version)});const x=S===void 0?null:D.exec(S),I=x===null?void 0:r.find(b=>b.version===Number(x[1]));return I===void 0?F():p(u,I,`${R}/v${I.version}`)}if(u.method!=="POST")return F();const O=performance.now();let s,l,w,A=!1,M=!1;const E=c(m=>{if(l!==void 0)try{e.onAnswer?.({...s===void 0?{}:{sub:s.sub,grantee:s.grantee,initiatingApp:s.initiatingApp,act:s.act,jti:s.jti,requestId:s.requestId,...s.runId===void 0?{}:{runId:s.runId}},...s===void 0&&y!==null&&y!==""?{requestId:y}:{},tool:l.path.slice(1).join(" "),status:m.status,durationMs:performance.now()-O,...w===void 0?{}:{receiptId:w.receipt_id},replayed:M})}catch{}return m},"answer");let y=u.headers.get("crtr-request-id");try{if(l=t?fe(r,g):ue(h.commands,g),e.token!==void 0&&!W(u.headers.get("authorization")??"",`Bearer ${e.token}`))return E($(401,{code:"unauthorized",message:"A valid bearer token is required.",next:"Send Authorization: Bearer <token>."}));const m=f===void 0?void 0:await f(u);if(l===void 0)return F();if(m!==void 0&&!m.scope.includes(l.scope)){s=Object.freeze({...m,requestId:y??"",...u.headers.get("crtr-run-id")?{runId:u.headers.get("crtr-run-id")}:{}});const a=l.leaf.group,v=i===void 0&&a!==void 0?m.scope.find(j=>j.endsWith(`:${a}`)&&j.length>a.length+1)?.slice(0,-(a.length+1)):void 0;throw oe(l.scope,v)}if(m!==void 0){if(y===null||y==="")return E($(400,{code:"invalid_request",message:"The request carries no Crtr-Request-Id header."}));const a=u.headers.get("crtr-run-id"),v=te(u.headers.get("crtr-caller"));s=Object.freeze({...m,requestId:y,...a!==null&&a!==""?{runId:a}:{},...Object.keys(v).length>0?{crtr:Object.freeze(v)}:{}})}const S=await le(u,l.manifest,m!==void 0);if(l.leaf.sideEffect){if(y===null||y==="")return E($(400,{code:"invalid_request",message:"The request carries no Crtr-Request-Id header."}));const a=s===void 0?null:{sub:s.sub,grantee:s.grantee};if(!await e.requests.record(y,a)){M=!0;const j=await ae(e.requests,y,a,l.plugin.timeouts?.requestMs??3e4);if(j!==!0){const B=j.headers.get("Crtr-Receipt");if(B!==null)try{w=JSON.parse(B)}catch{}return E(j)}M=!1}A=!0}const R=l;let x;const I={request:u,...s!==void 0?{caller:s}:{},approval:K(u),meter(a){if(s===void 0)throw new Error("ctx.meter needs an authenticated caller");if(w!==void 0)throw new Error("ctx.meter may be called only once");if(e.receipts===void 0)throw new Error("ctx.meter needs receipts with a provider and outbox");const v=R.path.slice(1).join(" "),j=a.usdEquivalent??(x?.mode==="unit"&&x.usd_per_unit!==null?a.units*x.usd_per_unit:0);return w={receipt_id:s.requestId,provider:e.receipts.provider,tool:v,sub:s.sub,grantee:s.grantee,payer:s.payer,units:a.units,unit:a.unit,usd_equivalent:j,request_id:s.requestId,...s.runId!==void 0?{run_id:s.runId}:{},time:new Date().toISOString()},w},async checkSpend(){if(s===void 0)throw new Error("ctx.checkSpend needs an authenticated caller");if(e.receipts===void 0)throw new Error("ctx.checkSpend needs receipts with a provider and outbox");if(e.receipts.ledger===void 0)throw new Error("ctx.checkSpend needs receipts.ledger (the provider's directory client credentials)");const a=/^Bearer ([^\s]+)$/.exec(u.headers.get("authorization")??"")?.[1];if(a===void 0)throw new Error("ctx.checkSpend needs the request's bearer token");x=(await Q(e.auth.issuer,a,R.path.slice(1).join(" "),e.receipts.ledger)).price}};let b,H;try{const a=await l.leaf.run(S,I);b=l.leaf.streaming?await we(a,e.onError,l.path):pe(a,l.manifest)}catch(a){a instanceof _?(b=P(a),H=a.headers):(q(e.onError,a,l.path),b=T(a))}if(A&&b.status===403&&await de(b))return await e.requests.delete(y),E(G(b,w));try{if(A)await e.requests.store(y,{status:b.status,body:await b.clone().text(),...H!==void 0?{headers:{...H}}:{},...w!==void 0?{receipt:w}:{}});else if(w!==void 0){if(e.receipts===void 0)throw new Error(`No receipt outbox for ${R.path.join(" ")}`);await e.receipts.outbox.save(w)}}catch(a){return q(e.onError,a,l.path,w),E(T(a))}return E(G(b,w))}catch(m){return m instanceof _?E(P(m)):(q(e.onError,m,l?.path??[]),E(T(m)))}}}c(Re,"createFetchHandler");function T(n){return $(500,{code:"handler_failed",message:n instanceof Error?n.message:String(n)})}c(T,"handlerFailed");function G(n,e){if(e===void 0)return n;const i=new Headers(n.headers);return i.set("Crtr-Receipt",JSON.stringify(e)),new Response(n.body,{status:n.status,headers:i})}c(G,"responseWithReceipt");async function de(n){try{const e=await n.clone().json();return z(e)&&z(e.error)&&e.error.code==="approval_required"}catch{return!1}}c(de,"isApprovalRequired");async function ae(n,e,i,t){for(;;){const r=await n.read(e);if(r===null){if(await n.record(e,i))return!0;continue}if(r.identity?.sub!==i?.sub||r.identity?.grantee!==i?.grantee)return $(403,{code:"provider_refused",message:"This request id belongs to a different caller."});if(r.answer!==null){const o=Y(r.answer.headers);return o.set("Content-Type","application/json"),r.answer.receipt!==void 0&&o.set("Crtr-Receipt",JSON.stringify(r.answer.receipt)),new Response(r.answer.body,{status:r.answer.status,headers:o})}if(Date.now()>=new Date(r.recordedAt).getTime()+t)return T(new Error("The earlier request has no stored answer."));await new Promise(o=>setTimeout(o,Math.min(25,Math.max(1,new Date(r.recordedAt).getTime()+t-Date.now()))))}}c(ae,"replay");function se(n,e,i){if(n.length===0)throw new Error("createFetchHandler needs at least one plugin definition");const t=n[0].name,r=new Map;for(const o of n){if(o.name!==t)throw new Error(`createFetchHandler serves one plugin; got ${t} and ${o.name}`);if(e&&o.version===void 0)throw new Error(`createFetchHandler needs an explicit version on every definition in an array; ${t} has none`);const f=o.version??1;if(r.has(f))throw new Error(`createFetchHandler got two definitions of ${t} major ${f}`);r.set(f,{version:f,plugin:o,commands:ce(o,i)})}return[...r.values()].sort((o,f)=>o.version-f.version)}c(se,"servedMajors");function ce(n,e){const t=ee(n,{mountPath:""}).mounts[0]?.node;if(t===void 0||t.kind!=="branch")throw new Error("Generated plugin manifest has no root branch");const r=new Map;return L(t.children,n.commands,[t.name],n,r,e),r}c(ce,"commandMap");function L(n,e,i,t,r,o){const f=Object.entries(e);if(n.length!==f.length)throw new Error("Generated plugin manifest does not match the command definition");for(const[d,[k,h]]of f.entries()){const p=n[d];if(p===void 0||p.kind!==h.kind)throw new Error(`Generated plugin manifest does not match command ${k}`);const u=[...i,p.name];if(p.kind==="branch"&&h.kind==="branch"){L(p.children,h.children,u,t,r,o);continue}if(p.kind==="leaf"&&h.kind==="leaf"){r.set(u.join("/"),{path:u,leaf:h,plugin:t,manifest:p,...h.group!==void 0?{scope:`${o??t.name}:${h.group}`}:{}});continue}throw new Error(`Generated plugin manifest does not match command ${k}`)}}c(L,"collectCommands");function ue(n,e){const i=U(e);for(let t=0;t<i.length;t+=1){const r=n.get(i.slice(t).join("/"));if(r!==void 0)return r}}c(ue,"findCommand");function fe(n,e){const i=U(e);for(let t=i.length-2;t>=0;t-=1){const r=D.exec(i[t]);if(r===null)continue;const f=n.find(d=>d.version===Number(r[1]))?.commands.get(i.slice(t+1).join("/"));if(f!==void 0)return f}}c(fe,"findVersionedCommand");function U(n){return n.split("/").filter(e=>e.length>0)}c(U,"pathSegments");async function le(n,e,i){const t=n.body===null?"":await n.text();let r;if(t.length===0)r={};else try{const d=JSON.parse(t);if(!z(d))throw new Error("not an object");r=d}catch{throw N("Request body must be a JSON object.",i?"body":void 0,i,t)}const o=new Map(e.params.map(d=>[d.name,d]));for(const d of Object.keys(r))if(!o.has(d))throw i?N(`Unknown parameter ${d}.`,d,!0,r[d]):new _({code:"unknown_parameter",message:`Unknown parameter ${d}.`,field:d});const f={};for(const d of e.params){if(!Object.hasOwn(r,d.name)){if(d.required)throw N(`Missing required parameter ${d.name}.`,d.name,i,null);continue}const k=r[d.name],h=he(k,d);if(h===void 0)throw N(`Invalid value for parameter ${d.name}.`,d.name,i,k);f[ge(d.name)]=h}return f}c(le,"decodeInput");function he(n,e){if(e.kind!=="stdin"&&e.kind!=="context-file"&&e.repeatable){if(!Array.isArray(n))return;const i=n.map(t=>V(t,e));return i.some(t=>t===void 0)?void 0:i}return V(n,e)}c(he,"decodeParameter");function V(n,e){if(e.kind==="stdin"||e.kind==="positional")return typeof n=="string"?n:void 0;if(e.kind!=="context-file"){if(e.type==="string")return typeof n=="string"?n:void 0;if(e.type==="int")return typeof n=="number"&&Number.isInteger(n)?n:void 0;if(e.type==="bool")return typeof n=="boolean"?n:void 0;if(e.type==="enum")return typeof n=="string"&&Array.isArray(e.choices)&&e.choices.includes(n)?n:void 0;if(e.type==="file")return typeof n=="string"?n:void 0;if(e.type==="path")return typeof n!="string"?void 0:e.encoding==="base64"?me(n):n}}c(V,"decodeScalar");function me(n){if(!(!/^[A-Za-z0-9+/]*={0,2}$/.test(n)||n.length%4!==0))try{const e=atob(n);return Uint8Array.from(e,i=>i.charCodeAt(0))}catch{return}}c(me,"decodeBase64");function pe(n,e){let i,t;try{i=JSON.stringify(n),t=JSON.parse(i)}catch{throw C("result")}if(!z(t))throw C("result");const r=X(e.output,t);if(r!==null)throw C(r.field,r.receivedType);for(const o of Object.keys(t))if(!e.output.some(f=>f.name===o))throw C(o);return new Response(i,{headers:{"Content-Type":"application/json"}})}c(pe,"resultResponse");async function we(n,e,i){if(!ye(n))throw C("result");const t=n[Symbol.asyncIterator](),r=new TextEncoder,o=await t.next();let f=o.done,d=f?void 0:r.encode(`${JSON.stringify(o.value)}
2
2
  `);const k=new ReadableStream({async pull(h){if(d!==void 0){h.enqueue(d),d=void 0;return}if(f){h.close();return}try{const p=await t.next();p.done?(f=!0,h.close()):h.enqueue(r.encode(`${JSON.stringify(p.value)}
3
- `))}catch(p){O(e,p,i),h.error(p)}},async cancel(h){await t.return?.(h)}});return new Response(k,{headers:{"Content-Type":"application/x-ndjson"}})}c(pe,"streamResponse");function O(n,e,i,t){try{n?.(e,i,t)}catch{}}c(O,"reportUnhandledError");function _(n,e){return new I({code:"handler_output_invalid",message:`Handler returned an invalid value for ${n}.`,status:500,field:n,...e!==void 0?{received:e}:{}})}c(_,"outputInvalid");function N(n,e,i=!1,t){return new I({code:i?"invalid_argument":"invalid_input",message:n,...e!==void 0?{field:e}:{},...i?{received:t}:{}})}c(N,"invalidInput");function F(){return v(404,{code:"no_such_command",message:"No command matches this request."})}c(F,"noSuchCommand");function J(n){return n==="/"?"":n.replace(/\/+$/,"")}c(J,"mountPath");function we(n){return n.replace(/-([a-z])/g,(e,i)=>i.toUpperCase())}c(we,"camel");function W(n,e){const i=new TextEncoder().encode(n),t=new TextEncoder().encode(e);let r=i.length^t.length;for(let o=0;o<Math.min(i.length,t.length);o+=1)r|=i[o]^t[o];return r===0}c(W,"constantTimeEqual");function M(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}c(M,"isRecord");function ge(n){return typeof n=="object"&&n!==null&&Symbol.asyncIterator in n}c(ge,"isAsyncIterable");export{Ae as createFetchHandler};
3
+ `))}catch(p){q(e,p,i),h.error(p)}},async cancel(h){await t.return?.(h)}});return new Response(k,{headers:{"Content-Type":"application/x-ndjson"}})}c(we,"streamResponse");function q(n,e,i,t){try{n?.(e,i,t)}catch{}}c(q,"reportUnhandledError");function C(n,e){return new _({code:"handler_output_invalid",message:`Handler returned an invalid value for ${n}.`,status:500,field:n,...e!==void 0?{received:e}:{}})}c(C,"outputInvalid");function N(n,e,i=!1,t){return new _({code:i?"invalid_argument":"invalid_input",message:n,...e!==void 0?{field:e}:{},...i?{received:t}:{}})}c(N,"invalidInput");function F(){return $(404,{code:"no_such_command",message:"No command matches this request."})}c(F,"noSuchCommand");function J(n){return n==="/"?"":n.replace(/\/+$/,"")}c(J,"mountPath");function ge(n){return n.replace(/-([a-z])/g,(e,i)=>i.toUpperCase())}c(ge,"camel");function W(n,e){const i=new TextEncoder().encode(n),t=new TextEncoder().encode(e);let r=i.length^t.length;for(let o=0;o<Math.min(i.length,t.length);o+=1)r|=i[o]^t[o];return r===0}c(W,"constantTimeEqual");function z(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}c(z,"isRecord");function ye(n){return typeof n=="object"&&n!==null&&Symbol.asyncIterator in n}c(ye,"isAsyncIterable");export{Re as createFetchHandler};
package/dist/verify.d.ts CHANGED
@@ -18,7 +18,7 @@ export interface FetchHandlerAuth {
18
18
  /** Validates `auth.providerId` and returns it bare, or undefined when none is configured. */
19
19
  export declare function normalizeProviderId(auth: FetchHandlerAuth): string | undefined;
20
20
  /** Turns a request's bearer into the verified caller, or throws the 401 `LeafError`. */
21
- export type CallerVerifier = (request: Request) => Promise<Omit<LeafCaller, 'requestId' | 'runId'>>;
21
+ export type CallerVerifier = (request: Request) => Promise<Omit<LeafCaller, 'requestId' | 'runId' | 'crtr'>>;
22
22
  export declare function createCallerVerifier(auth: FetchHandlerAuth): CallerVerifier;
23
23
  /** The 403 for a leaf whose `<provider>:<group>` the verified token does not carry. */
24
24
  export declare function scopeMissing(scope: string, heldByOtherProvider?: string): LeafError;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/plugin",
3
- "version": "0.3.490",
3
+ "version": "0.3.492",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -23,8 +23,8 @@
23
23
  "postpack": "node ../../scripts/pack-minify.mjs postpack packages/crouter-plugin"
24
24
  },
25
25
  "dependencies": {
26
- "@north-light/api": "0.3.490",
27
- "@north-light/identity": "0.3.490"
26
+ "@north-light/api": "0.3.492",
27
+ "@north-light/identity": "0.3.492"
28
28
  },
29
29
  "license": "UNLICENSED",
30
30
  "publishConfig": {