@frontmcp/adapters 1.11.0 → 1.11.1-rc.1
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/esm/index.mjs +2 -2
- package/esm/openapi/index.mjs +2 -2
- package/esm/package.json +6 -6
- package/esm/skills/index.mjs +1 -1
- package/index.js +2 -2
- package/openapi/index.js +2 -2
- package/openapi/openapi.adapter.d.ts.map +1 -1
- package/openapi/openapi.output-schema.d.ts +11 -0
- package/openapi/openapi.output-schema.d.ts.map +1 -0
- package/openapi/openapi.utils.d.ts.map +1 -1
- package/package.json +6 -6
- package/skills/bundle/bundle-diff.d.ts.map +1 -1
- package/skills/bundle/bundle.schema.d.ts +1 -0
- package/skills/bundle/bundle.schema.d.ts.map +1 -1
- package/skills/bundle/bundle.types.d.ts +2 -0
- package/skills/bundle/bundle.types.d.ts.map +1 -1
- package/skills/bundle/overlay-parser.d.ts.map +1 -1
- package/skills/index.d.ts +3 -3
- package/skills/index.d.ts.map +1 -1
- package/skills/index.js +1 -1
- package/skills/openapi/compile-skilled-bundle.d.ts +1 -0
- package/skills/openapi/compile-skilled-bundle.d.ts.map +1 -1
- package/skills/security/jwt-verifier.d.ts +1 -1
- package/skills/security/jwt-verifier.d.ts.map +1 -1
- package/skills/security/webhook-replay-guard.d.ts +3 -0
- package/skills/security/webhook-replay-guard.d.ts.map +1 -1
- package/skills/source-options.d.ts.map +1 -1
- package/skills/sources/index.d.ts +3 -3
- package/skills/sources/index.d.ts.map +1 -1
- package/skills/sources/saas-pull.source.d.ts +50 -12
- package/skills/sources/saas-pull.source.d.ts.map +1 -1
- package/skills/sources/skill-bundle-source.interface.d.ts +21 -4
- package/skills/sources/skill-bundle-source.interface.d.ts.map +1 -1
- package/skills/sources/static.source.d.ts +6 -2
- package/skills/sources/static.source.d.ts.map +1 -1
package/skills/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:true}):target,mod));var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var skills_exports={};__export(skills_exports,{BundlePinnedError:()=>BundlePinnedError,BundlePushJwtVerifier:()=>BundlePushJwtVerifier,BundleStore:()=>BundleStore,ClassificationRegistry:()=>ClassificationRegistry,FilesystemSkillsSource:()=>FilesystemSkillsSource,Hs256AuditSigner:()=>Hs256AuditSigner,InlineSource:()=>InlineSource,MemoryAuditStore:()=>MemoryAuditStore,NpmSource:()=>NpmSource,OverlayParseError:()=>OverlayParseError,Rs256AuditSigner:()=>Rs256AuditSigner,SKILL_AUDIT_ERROR_MESSAGE_MAX:()=>SKILL_AUDIT_ERROR_MESSAGE_MAX,SKILL_AUDIT_GENESIS_PREV_HASH:()=>SKILL_AUDIT_GENESIS_PREV_HASH,SKILL_AUDIT_KEYS:()=>SKILL_AUDIT_KEYS,SKILL_AUDIT_QUEUE_MAX:()=>SKILL_AUDIT_QUEUE_MAX,SaasPullSource:()=>SaasPullSource,SkillAuditWriter:()=>SkillAuditWriter,SkillAuditWriterToken:()=>SkillAuditWriterToken,SkillDependencyCycleError:()=>SkillDependencyCycleError,SkillDependencyInvariantError:()=>SkillDependencyInvariantError,SkillDependencyMissingError:()=>SkillDependencyMissingError,StaticSource:()=>StaticSource,StorageAdapterAuditStore:()=>StorageAdapterAuditStore,WebhookReplayGuard:()=>WebhookReplayGuard,applyClassificationOverrides:()=>applyClassificationOverrides,applyEnvironmentOverlay:()=>applyEnvironmentOverlay,buildFrontMcpConfigFromManifest:()=>buildFrontMcpConfigFromManifest,buildKnownOps:()=>buildKnownOps,buildResourceChangeNotification:()=>buildResourceChangeNotification,bundleDigest:()=>bundleDigest,bundleSkillToActions:()=>bundleSkillToActions,bundleSourceSchema:()=>bundleSourceSchema,canonicalize:()=>canonicalize,canonicalizeRecordForSigning:()=>canonicalizeRecordForSigning,classifyOne:()=>classifyOne,classifyOperations:()=>classifyOperations,compileSkilledBundleFromOpenApi:()=>compileSkilledBundleFromOpenApi,createBundleSource:()=>createBundleSource,createSkillAuditMetrics:()=>createSkillAuditMetrics,crossValidate:()=>crossValidate,crossValidateManifest:()=>crossValidateManifest,dedupeOpReferences:()=>dedupeOpReferences,defaultAuditSignatureVerifier:()=>defaultAuditSignatureVerifier,deployManifestSchema:()=>deployManifestSchema,diffBundles:()=>diffBundles,extractOpReferences:()=>extractOpReferences,formatDiffSummary:()=>formatDiffSummary,hasSkillAuditFactory:()=>import_sdk.hasSkillAuditFactory,linkRecord:()=>linkRecord,loadFilesystemSkill:()=>loadFilesystemSkill,nextPrevHash:()=>nextPrevHash,npmSourceSchema:()=>npmSourceSchema,parseOverlay:()=>parseOverlay,parseSkillFrontmatter:()=>parseSkillFrontmatter,renderResourceUri:()=>renderResourceUri,resolveSkillLoadOrder:()=>resolveSkillLoadOrder,resolvedBundleSchema:()=>resolvedBundleSchema,saasSourceSchema:()=>saasSourceSchema,setSkillAuditFactory:()=>import_sdk.setSkillAuditFactory,signatureKeySchema:()=>signatureKeySchema,staticSourceSchema:()=>staticSourceSchema,validateOpReferences:()=>validateOpReferences,verifyBundleSignature:()=>verifyBundleSignature,verifyChain:()=>verifyChain});module.exports=__toCommonJS(skills_exports);function bundleSkillToActions(skill,ops){return skill.operationIds.map(opId=>{const op=ops[opId];if(!op){throw new Error(`bundleSkillToActions: skill "${skill.id}" references unknown operationId "${opId}"`)}const action={actionId:op.operationId,summary:op.summary??`${op.httpMethod} ${op.pathTemplate}`,...op.description!==void 0&&{description:op.description},inputJsonSchema:op.inputSchema,outputJsonSchema:op.outputSchema,...op.requiredAuthorities!==void 0&&{requiredAuthorities:op.requiredAuthorities}};return action})}var import_lazy_zod=require("@frontmcp/lazy-zod");var httpMethodSchema=import_lazy_zod.z.enum(["GET","POST","PUT","PATCH","DELETE","HEAD"]);var RFC7230_TOKEN=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;var pathTemplateRegex=/^\/[^\s?#]*$/;var FORBIDDEN_PATH_FRAGMENTS=["..","\\","`","$(","${"];var pathTemplateSchema=import_lazy_zod.z.string().min(1).max(2048).refine(v=>pathTemplateRegex.test(v),{message:"pathTemplate must start with `/` and contain no whitespace, `?`, or `#`"}).refine(v=>!FORBIDDEN_PATH_FRAGMENTS.some(bad=>v.includes(bad)),{message:"pathTemplate contains a forbidden character or fragment (`..`, backtick, `$(`, etc.)"});var jsonSchemaShape=import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown());var authoritiesPolicySchema=import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown());var authBindingSchema=import_lazy_zod.z.discriminatedUnion("kind",[import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("none")}).strict(),import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("bearer"),vaultRef:import_lazy_zod.z.string().min(1).max(256),passthroughCallerToken:import_lazy_zod.z.boolean().optional()}).strict(),import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("apiKey"),in:import_lazy_zod.z.enum(["header","query"]),name:import_lazy_zod.z.string().min(1).max(128).regex(RFC7230_TOKEN,"apiKey name must match RFC 7230 token grammar"),vaultRef:import_lazy_zod.z.string().min(1).max(256)}).strict(),import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("oauth2"),flow:import_lazy_zod.z.literal("client_credentials"),vaultRef:import_lazy_zod.z.string().min(1).max(256)}).strict()]);var serviceDescriptorSchema=import_lazy_zod.z.object({id:import_lazy_zod.z.string().min(1).max(128).regex(/^[a-zA-Z0-9_-]+$/,"service id must be alphanumeric / `-` / `_`"),baseUrl:import_lazy_zod.z.string().url().max(2048),description:import_lazy_zod.z.string().max(4096).optional()}).strict();var parameterMapperSchema=import_lazy_zod.z.object({inputKey:import_lazy_zod.z.string().min(1).max(256),type:import_lazy_zod.z.enum(["path","query","header","cookie","body"]),key:import_lazy_zod.z.string().min(1).max(256),required:import_lazy_zod.z.boolean().optional(),style:import_lazy_zod.z.string().max(64).optional(),explode:import_lazy_zod.z.boolean().optional(),serialization:import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown()).optional(),security:import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown()).optional()}).passthrough().refine(mapper=>mapper.type!=="header"&&mapper.type!=="cookie"||RFC7230_TOKEN.test(mapper.key),{message:"a header or cookie mapper key must match RFC 7230 token grammar",path:["key"]});var operationDescriptorSchema=import_lazy_zod.z.object({operationId:import_lazy_zod.z.string().min(1).max(256).regex(/^[a-zA-Z0-9_.\-:]+$/,"operationId must be alphanumeric / `-` / `_` / `.` / `:`"),serviceId:import_lazy_zod.z.string().min(1).max(128),httpMethod:httpMethodSchema,pathTemplate:pathTemplateSchema,inputSchema:jsonSchemaShape,outputSchema:jsonSchemaShape,mapper:import_lazy_zod.z.array(parameterMapperSchema).max(256),authBindingRef:import_lazy_zod.z.string().min(1).max(128),requiredAuthorities:authoritiesPolicySchema.optional(),public:import_lazy_zod.z.boolean().optional(),maxResponseBytes:import_lazy_zod.z.number().int().positive().max(64*1024*1024).optional(),timeoutMs:import_lazy_zod.z.number().int().positive().max(6e5).optional(),summary:import_lazy_zod.z.string().max(512).optional(),description:import_lazy_zod.z.string().max(8192).optional()}).strict();var bundledSkillSchema=import_lazy_zod.z.object({id:import_lazy_zod.z.string().min(1).max(128).regex(/^[a-zA-Z0-9_.-]+$/),name:import_lazy_zod.z.string().min(1).max(256),description:import_lazy_zod.z.string().max(2048),instructions:import_lazy_zod.z.string().max(256*1024),tags:import_lazy_zod.z.array(import_lazy_zod.z.string().max(64)).max(64).optional(),operationIds:import_lazy_zod.z.array(import_lazy_zod.z.string().min(1).max(256)).min(0).max(2e3),requiredAuthorities:authoritiesPolicySchema.optional(),requires:import_lazy_zod.z.array(import_lazy_zod.z.string().min(1).max(128)).max(64).optional()}).strict();var integritySchema=import_lazy_zod.z.object({alg:import_lazy_zod.z.enum(["RS256","EdDSA"]),keyId:import_lazy_zod.z.string().min(1).max(256),signature:import_lazy_zod.z.string().min(1).max(8192),digest:import_lazy_zod.z.string().min(1).max(256)}).strict();var resolvedBundleSchema=import_lazy_zod.z.object({schemaVersion:import_lazy_zod.z.literal(1),bundleId:import_lazy_zod.z.string().min(1).max(256).regex(/^[a-zA-Z0-9_.\-:]+$/),version:import_lazy_zod.z.string().min(1).max(256),generatedAt:import_lazy_zod.z.string().min(1).refine(v=>!Number.isNaN(Date.parse(v)),{message:"generatedAt must be ISO 8601 parsable"}),sourceDigest:import_lazy_zod.z.string().min(1).max(256).regex(/^[a-fA-F0-9]+$/,"sourceDigest must be a hex sha256"),services:import_lazy_zod.z.array(serviceDescriptorSchema).min(1).max(64),authBindings:import_lazy_zod.z.record(import_lazy_zod.z.string().min(1).max(128),authBindingSchema),skills:import_lazy_zod.z.array(bundledSkillSchema).max(1e3),operations:import_lazy_zod.z.record(import_lazy_zod.z.string().min(1).max(256),operationDescriptorSchema),integrity:integritySchema.optional()}).strict();function crossValidate(bundle){const errors=[];const serviceIds=new Set(bundle.services.map(s=>s.id));const authRefs=new Set(Object.keys(bundle.authBindings));const opIds=new Set(Object.keys(bundle.operations));for(const[opId,op]of Object.entries(bundle.operations)){if(op.operationId!==opId){errors.push(`operation key "${opId}" does not match operationId "${op.operationId}"`)}if(!serviceIds.has(op.serviceId)){errors.push(`operation "${opId}" references unknown service "${op.serviceId}"`)}if(!authRefs.has(op.authBindingRef)){errors.push(`operation "${opId}" references unknown authBinding "${op.authBindingRef}"`)}}const seenSkillIds=new Set;for(const skill of bundle.skills){if(seenSkillIds.has(skill.id)){errors.push(`duplicate skill id "${skill.id}"`)}seenSkillIds.add(skill.id);for(const opId of skill.operationIds){if(!opIds.has(opId)){errors.push(`skill "${skill.id}" references unknown operationId "${opId}"`)}}}return errors.length===0?{ok:true}:{ok:false,errors}}var yaml=__toESM(require("js-yaml"));var OVERLAY_BUNDLE_KEY="x-frontmcp-bundle";var OverlayParseError=class extends Error{constructor(message,errors){super(message);this.errors=errors;this.name="OverlayParseError"}errors};function parseRaw(input){switch(input.kind){case"yaml":try{return yaml.load(input.content)}catch(e){throw new OverlayParseError(`Overlay YAML parse failed: ${e.message}`)}case"json":try{return JSON.parse(input.content)}catch(e){throw new OverlayParseError(`Overlay JSON parse failed: ${e.message}`)}case"object":return input.content}}function extractBundle(overlay){if(overlay===null||typeof overlay!=="object"){throw new OverlayParseError("Overlay root must be an object")}const root=overlay;if(OVERLAY_BUNDLE_KEY in root){return root[OVERLAY_BUNDLE_KEY]}const info=root["info"];if(info&&typeof info==="object"&&OVERLAY_BUNDLE_KEY in info){return info[OVERLAY_BUNDLE_KEY]}if("schemaVersion"in root&&"bundleId"in root){return root}throw new OverlayParseError("Overlay does not contain `info.x-frontmcp-bundle` or a bare bundle object with schemaVersion + bundleId")}function parseOverlay(input){const raw=parseRaw(input);const bundleCandidate=extractBundle(raw);const result=resolvedBundleSchema.safeParse(bundleCandidate);if(!result.success){const errors=result.error.issues.map(i=>`${i.path.join(".")||"<root>"}: ${i.message}`);throw new OverlayParseError(`Bundle schema validation failed (${errors.length} issue(s))`,errors)}const cross=crossValidate(result.data);if(!cross.ok){throw new OverlayParseError(`Bundle cross-reference validation failed (${cross.errors.length} issue(s))`,cross.errors)}return result.data}var opEqual=(a,b)=>a.operationId===b.operationId&&a.serviceId===b.serviceId&&a.httpMethod===b.httpMethod&&a.pathTemplate===b.pathTemplate&&a.authBindingRef===b.authBindingRef&&JSON.stringify(a.mapper??null)===JSON.stringify(b.mapper??null)&&JSON.stringify(a.inputSchema)===JSON.stringify(b.inputSchema)&&JSON.stringify(a.outputSchema)===JSON.stringify(b.outputSchema)&&JSON.stringify(a.requiredAuthorities??null)===JSON.stringify(b.requiredAuthorities??null)&&(a.public??false)===(b.public??false)&&a.maxResponseBytes===b.maxResponseBytes&&a.timeoutMs===b.timeoutMs;var skillEqual=(a,b)=>a.id===b.id&&a.name===b.name&&a.description===b.description&&a.instructions===b.instructions&&JSON.stringify(a.tags??[])===JSON.stringify(b.tags??[])&&JSON.stringify(a.operationIds)===JSON.stringify(b.operationIds)&&JSON.stringify(a.requiredAuthorities??null)===JSON.stringify(b.requiredAuthorities??null);function diffBundles(oldBundle,newBundle){const oldSkills=new Map((oldBundle?.skills??[]).map(s=>[s.id,s]));const newSkills=new Map(newBundle.skills.map(s=>[s.id,s]));const addedSkillIds=[];const removedSkillIds=[];const changedSkillIds=[];for(const[id,skill]of newSkills){const prev=oldSkills.get(id);if(!prev)addedSkillIds.push(id);else if(!skillEqual(prev,skill))changedSkillIds.push(id)}for(const id of oldSkills.keys()){if(!newSkills.has(id))removedSkillIds.push(id)}const oldOps=oldBundle?.operations??{};const newOps=newBundle.operations;const addedOperationIds=[];const removedOperationIds=[];const changedOperationIds=[];for(const[id,op]of Object.entries(newOps)){const prev=oldOps[id];if(!prev)addedOperationIds.push(id);else if(!opEqual(prev,op))changedOperationIds.push(id)}for(const id of Object.keys(oldOps)){if(!(id in newOps))removedOperationIds.push(id)}const isNoOp=!!oldBundle&&oldBundle.version===newBundle.version&&addedSkillIds.length===0&&removedSkillIds.length===0&&changedSkillIds.length===0&&addedOperationIds.length===0&&removedOperationIds.length===0&&changedOperationIds.length===0;return{addedSkillIds,removedSkillIds,changedSkillIds,addedOperationIds,removedOperationIds,changedOperationIds,isNoOp}}function formatDiffSummary(diff){if(diff.isNoOp)return"no-op";return[`+${diff.addedSkillIds.length}/-${diff.removedSkillIds.length}/~${diff.changedSkillIds.length} skills`,`+${diff.addedOperationIds.length}/-${diff.removedOperationIds.length}/~${diff.changedOperationIds.length} ops`].join(", ")}var KNOWN_BUNDLE_SOURCES=["static","npm","saas-pull","webhook","filesystem","unknown"];function normalizeBundleSource(value){if(typeof value!=="string")return"unknown";return KNOWN_BUNDLE_SOURCES.includes(value)?value:"unknown"}function classifySwapError(err){if(err instanceof BundlePinnedError)return"pinned";if(err instanceof Error){const name=err.name;const msg=err.message.toLowerCase();if(name==="BundlePinnedError")return"pinned";if(msg.includes("bundle is required")||msg.includes("malformed"))return"invalid_bundle";if(msg.includes("listener"))return"listener_error"}return"unknown"}var BundleStore=class{active;listeners=new Set;history=[];pinnedVersion;historySize;telemetry;pullsCounter;constructor(options={}){const requested=options.historySize??3;if(!Number.isInteger(requested)||requested<1){throw new Error("BundleStore: historySize must be a positive integer")}this.historySize=requested;this.telemetry=options.telemetry;this.pullsCounter=this.telemetry?.createCounter("frontmcp_skills_bundle_pulls_total","Number of bundle pulls (swaps) attempted, partitioned by status / source.")}current(){return this.active}swap(next,source){if(!next){throw new Error("BundleStore.swap: bundle is required")}const sourceLabel=normalizeBundleSource(source);if(this.telemetry){const span=this.telemetry.startSpan("skill.bundle.swap",{source:sourceLabel,bundle_id:next.bundleId,version:next.version,skill_count:next.skills.length,...this.active?.version?{from_version:this.active.version}:{}});try{if(this.pinnedVersion!==void 0){throw new BundlePinnedError(this.pinnedVersion)}const diff=this.commit(next,"normal");span.setAttributes({status:"ok",is_no_op:diff.isNoOp});span.end();this.pullsCounter?.inc(1,{status:"ok",source:sourceLabel});return diff}catch(err){const reason=classifySwapError(err);if(span.endWithError){span.endWithError(err instanceof Error?err:String(err))}else{span.recordError?.(err instanceof Error?err:new Error(String(err)));span.end()}this.pullsCounter?.inc(1,{status:"error",source:sourceLabel,reason});throw err}}if(this.pinnedVersion!==void 0){throw new BundlePinnedError(this.pinnedVersion)}return this.commit(next,"normal")}pin(version){if(this.active&&this.active.version===version){this.pinnedVersion=version;return void 0}const entry=this.history.find(h=>h.bundle.version===version);if(!entry){throw new Error(`BundleStore.pin: version "${version}" not in history`)}const diff=this.commit(entry.bundle,"pin");this.pinnedVersion=version;return diff}rollback(){const prior=this.history[this.history.length-1];if(!prior){throw new Error("BundleStore.rollback: no prior bundle in history")}this.pinnedVersion=void 0;return this.commit(prior.bundle,"rollback")}unpin(){this.pinnedVersion=void 0}isPinned(){return this.pinnedVersion!==void 0}pinned(){return this.pinnedVersion}historySnapshot(){return this.history.map(h=>({version:h.bundle.version,bundleId:h.bundle.bundleId,validatedAt:h.validatedAt}))}subscribe(fn){this.listeners.add(fn);return()=>{this.listeners.delete(fn)}}reset(){this.active=void 0;this.history=[];this.pinnedVersion=void 0}commit(next,reason){const previous=this.active;const diff=diffBundles(previous,next);if(previous){const lastInHistory=this.history[this.history.length-1];if(!lastInHistory||lastInHistory.bundle.version!==previous.version){this.history.push({bundle:previous,validatedAt:Date.now()});if(this.history.length>this.historySize){this.history.splice(0,this.history.length-this.historySize)}}}this.active=next;for(const fn of this.listeners){try{fn({previous,current:next,diff,reason})}catch{}}return diff}};var BundlePinnedError=class extends Error{constructor(pinnedVersion){super(`BundleStore is pinned to version "${pinnedVersion}"; new swaps are rejected until unpin().`);this.pinnedVersion=pinnedVersion;this.name="BundlePinnedError"}pinnedVersion};var SkillDependencyCycleError=class extends Error{constructor(cycle){super(`skill dependency cycle: ${cycle.join(" -> ")}`);this.cycle=cycle;this.name="SkillDependencyCycleError"}cycle};var SkillDependencyMissingError=class extends Error{constructor(skillId,missingId){super(`skill "${skillId}" requires unknown skill "${missingId}"`);this.skillId=skillId;this.missingId=missingId;this.name="SkillDependencyMissingError"}skillId;missingId};var SkillDependencyInvariantError=class extends Error{constructor(message){super(message);this.name="SkillDependencyInvariantError"}};function resolveSkillLoadOrder(skills){const byId=new Map;for(const skill of skills){if(byId.has(skill.id)){byId.set(skill.id,skill);continue}byId.set(skill.id,skill)}const inDegree=new Map;const requiredBy=new Map;for(const id of byId.keys()){inDegree.set(id,0);requiredBy.set(id,[])}for(const skill of byId.values()){const deps=skill.requires??[];for(const depId of deps){if(!byId.has(depId)){throw new SkillDependencyMissingError(skill.id,depId)}inDegree.set(skill.id,(inDegree.get(skill.id)??0)+1);const reverse=requiredBy.get(depId)??[];reverse.push(skill.id);requiredBy.set(depId,reverse)}}const ready=[...byId.keys()].filter(id=>inDegree.get(id)===0).sort();const order=[];while(ready.length>0){const next=ready.shift();if(next===void 0){throw new SkillDependencyInvariantError("resolveSkillLoadOrder: invariant broken \u2014 ready queue drained mid-iteration")}const nextSkill=byId.get(next);if(!nextSkill){throw new SkillDependencyInvariantError(`resolveSkillLoadOrder: invariant broken \u2014 id "${next}" missing from byId map`)}order.push(nextSkill);for(const dependent of requiredBy.get(next)??[]){const remaining=(inDegree.get(dependent)??0)-1;inDegree.set(dependent,remaining);if(remaining===0){let i=0;while(i<ready.length&&ready[i]<dependent)i++;ready.splice(i,0,dependent)}}}if(order.length===byId.size){return order}const remainingIds=[...byId.keys()].filter(id=>(inDegree.get(id)??0)>0);const cycle=findCycle(remainingIds,byId);throw new SkillDependencyCycleError(cycle)}function findCycle(remaining,byId){const adj=new Map;for(const id of remaining){adj.set(id,(byId.get(id)?.requires??[]).filter(d=>remaining.includes(d)))}const WHITE=0;const GRAY=1;const BLACK=2;const color=new Map;for(const id of remaining)color.set(id,WHITE);const stack=[];function dfs(node){color.set(node,GRAY);stack.push(node);for(const next of adj.get(node)??[]){const c=color.get(next);if(c===GRAY){const startIdx=stack.indexOf(next);return[...stack.slice(startIdx),next]}if(c===WHITE){const found=dfs(next);if(found)return found}}color.set(node,BLACK);stack.pop();return void 0}for(const id of remaining){if(color.get(id)===WHITE){const cycle=dfs(id);if(cycle)return cycle}}return remaining}var import_lazy_zod2=require("@frontmcp/lazy-zod");var httpsUrl=import_lazy_zod2.z.string().url().refine(v=>{try{return new URL(v).protocol==="https:"}catch{return false}},{message:"must use https://"});var staticSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("static"),path:import_lazy_zod2.z.string().min(1),watch:import_lazy_zod2.z.boolean().default(false)});var npmSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("npm"),packageName:import_lazy_zod2.z.string().min(1),exportName:import_lazy_zod2.z.string().optional(),verifyProvenance:import_lazy_zod2.z.boolean().default(true)});var saasSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("saas"),endpoint:httpsUrl,authToken:import_lazy_zod2.z.string().min(1),expectedAudience:import_lazy_zod2.z.string().min(1),pollIntervalMs:import_lazy_zod2.z.number().int().positive().default(3e5),enableWebhook:import_lazy_zod2.z.boolean().default(false),jwksUrl:httpsUrl,expectedIssuer:import_lazy_zod2.z.string().min(1)});var inlineSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("inline"),content:import_lazy_zod2.z.unknown()});var bundleSourceSchema=import_lazy_zod2.z.discriminatedUnion("type",[staticSourceSchema,npmSourceSchema,saasSourceSchema,inlineSourceSchema]);var signatureKeySchema=import_lazy_zod2.z.object({keyId:import_lazy_zod2.z.string().min(1),alg:import_lazy_zod2.z.enum(["RS256","EdDSA"]),publicKeyPem:import_lazy_zod2.z.string().min(1)});var InlineSource=class{constructor(options,logger){this.options=options;this.logger=logger}options;logger;id="inline:bundle";listeners=new Set;async start(){const bundle=this.parse();for(const fn of this.listeners){try{fn(bundle)}catch(e){this.logger.warn(`[inline-source] listener threw: ${e.message}`)}}}onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async stop(){this.listeners.clear()}parse(){return parseOverlay({kind:"object",content:this.options.content})}};var npmDynamicImport;var NpmSource=class{constructor(options,logger){this.options=options;this.logger=logger;this.id=`npm:${this.options.packageName}${this.options.exportName?`#${this.options.exportName}`:""}`}options;logger;id;listeners=new Set;cached;async start(){const bundle=await this.loadBundleFromPackage();this.cached=bundle;this.notify(bundle)}onChange(listener){this.listeners.add(listener);if(this.cached){try{listener(this.cached)}catch(e){this.logger.warn(`[npm-source] listener threw on replay: ${e.message}`)}}return()=>{this.listeners.delete(listener)}}async stop(){this.listeners.clear()}async loadBundleFromPackage(){if(this.options.verifyProvenance){throw new Error(`npm source "${this.options.packageName}": verifyProvenance is true, but npm provenance verification is not implemented, so the package was not loaded. Set verifyProvenance: false to load it without a provenance check, only for a package you trust: importing it runs its code before any bundle signature is checked, so pin its exact version and install it from a lockfile with integrity hashes.`)}const mod=await this.dynamicImport(this.options.packageName);const exportName=this.options.exportName??"default";const modRecord=mod;const exported=modRecord[exportName]??(exportName==="default"?mod:void 0);if(exported===void 0||exported===null){throw new Error(`npm source "${this.options.packageName}": export "${exportName}" not found on the package`)}return parseOverlay({kind:"object",content:exported})}dynamicImport(specifier){npmDynamicImport??=new Function("s","return import(s)");return npmDynamicImport(specifier)}notify(bundle){for(const fn of this.listeners){try{fn(bundle)}catch(e){this.logger.warn(`[npm-source] listener threw: ${e.message}`)}}}};var import_auth=require("@frontmcp/auth");var import_utils=require("@frontmcp/utils");var DEFAULT_CACHE_DIR=".frontmcp/skilled-openapi";var DEFAULT_PULL_TIMEOUT_MS=3e4;var USER_AGENT="frontmcp-skilled-openapi";var SaasPullTokenRejectedError=class extends Error{constructor(reason){super(`[saas-source] pull token rejected: ${reason}`);this.name="SaasPullTokenRejectedError"}};function claimValues(claim){if(Array.isArray(claim))return claim;return claim===void 0?[]:[claim]}var PUBLIC_KEY_MEMBERS=new Map([["RSA",["n","e"]],["EC",["crv","x","y"]],["OKP",["crv","x"]]]);function isUsableSigningKey(key){if(typeof key!=="object"||key===null)return false;const jwk=key;const members=typeof jwk["kty"]==="string"?PUBLIC_KEY_MEMBERS.get(jwk["kty"]):void 0;if(!members||jwk["use"]!==void 0&&jwk["use"]!=="sig")return false;return members.every(m=>typeof jwk[m]==="string"&&jwk[m]!=="")}var SaasPullSource=class{constructor(options,cacheDir,logger,deps={}){this.options=options;this.cacheDir=cacheDir;this.logger=logger;this.deps=deps;this.id=`saas:${this.options.endpoint}`}options;cacheDir;logger;deps;id;listeners=new Set;pollHandle;inFlight=false;stopped=false;jwksService=new import_auth.JwksService({});async start(){if(this.stopped||this.inFlight){this.schedulePoll();return}this.inFlight=true;let bundle;try{try{bundle=await this.fetchOnce();await this.persistCache(bundle)}catch(e){if(e instanceof SaasPullTokenRejectedError)throw e;this.logger.warn(`[saas-source] initial pull failed (${e.message}); attempting cached bundle fallback`);bundle=await this.loadCache();if(!bundle){throw new Error(`[saas-source] initial pull failed and no cached bundle is available ${this.cacheLocation()}: ${e.message}`,{cause:e})}this.logger.warn(`[saas-source] using cached bundle "${bundle.bundleId}@${bundle.version}"`)}this.notify(bundle)}finally{this.inFlight=false}this.schedulePoll()}onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async stop(){this.stopped=true;if(this.pollHandle){clearTimeout(this.pollHandle);this.pollHandle=void 0}this.listeners.clear()}schedulePoll(){if(this.stopped||this.deps.disablePolling)return;if(this.pollHandle)clearTimeout(this.pollHandle);this.pollHandle=setTimeout(()=>{void this.pollOnce()},this.options.pollIntervalMs);this.pollHandle.unref?.()}async refresh(){if(this.stopped||this.inFlight)return void 0;this.inFlight=true;try{const bundle=await this.fetchOnce();await this.persistCache(bundle);this.notify(bundle);return bundle}finally{this.inFlight=false}}async pollOnce(){if(this.stopped)return;if(this.inFlight){this.schedulePoll();return}this.inFlight=true;try{const bundle=await this.fetchOnce();await this.persistCache(bundle);this.notify(bundle)}catch(e){if(e instanceof SaasPullTokenRejectedError){this.logger.error(`[saas-source] poll refused: ${e.message}; keeping the current bundle`)}else{this.logger.warn(`[saas-source] poll failed: ${e.message}`)}}finally{this.inFlight=false;this.schedulePoll()}}async httpGet(url,headers){const controller=new AbortController;const timer=setTimeout(()=>controller.abort(),DEFAULT_PULL_TIMEOUT_MS);try{const res=await fetch(url,{method:"GET",headers,signal:controller.signal,redirect:"manual"});if((0,import_utils.isRedirectResponse)(res)){throw new Error(`pull endpoint returned a redirect (${res.status}); not followed to protect the bearer token`)}const body=await res.text();return{status:res.status,body}}finally{clearTimeout(timer)}}async verifyPullToken(){const{jwksUrl,expectedIssuer,expectedAudience,authToken}=this.options;const{status,body}=await this.httpGet(jwksUrl,{Accept:"application/json","User-Agent":USER_AGENT});if(status<200||status>=300){throw new Error(`JWKS ${jwksUrl} responded ${status}`)}let jwks;try{jwks=JSON.parse(body)}catch{throw new Error(`JWKS ${jwksUrl} is not valid JSON`)}if(!jwks||!Array.isArray(jwks.keys)||jwks.keys.length===0){throw new Error(`JWKS ${jwksUrl} has no keys`)}const signingKeys=jwks.keys.filter(isUsableSigningKey);if(signingKeys.length===0){throw new Error(`JWKS ${jwksUrl} has no usable signing keys`)}const verified=await this.jwksService.verifyTransparentToken(authToken,[{id:"skilled-openapi-saas",issuerUrl:expectedIssuer,jwks:{keys:signingKeys}}]);if(!verified.ok||!verified.payload){throw new SaasPullTokenRejectedError(`not a valid token from issuer "${expectedIssuer}" signed by a key in ${jwksUrl} (${verified.error??"verification failed"})`)}const payload=verified.payload;if(!claimValues(payload["aud"]).includes(expectedAudience)){throw new SaasPullTokenRejectedError(`its "aud" claim does not include "${expectedAudience}"`)}const resource=claimValues(payload["resource"]);if(resource.length>0&&!resource.includes(expectedAudience)){throw new SaasPullTokenRejectedError(`its "resource" claim does not include "${expectedAudience}"`)}}async fetchOnce(){await this.verifyPullToken();const{status,body}=await this.httpGet(this.options.endpoint,{Authorization:`Bearer ${this.options.authToken}`,Accept:"application/json, application/yaml","User-Agent":USER_AGENT});if(status<200||status>=300){throw new Error(`pull responded ${status}`)}const trimmed=body.trimStart();return parseOverlay(trimmed.startsWith("{")||trimmed.startsWith("[")?{kind:"json",content:body}:{kind:"yaml",content:body})}cacheLocation(){return this.deps.cache?"in the injected bundle cache":`at ${this.cachePath()}`}cachePath(){const dir=this.cacheDir??DEFAULT_CACHE_DIR;return(0,import_utils.pathResolve)(dir,`${this.options.expectedAudience.replace(/[^a-zA-Z0-9_.-]/g,"_")}.json`)}async persistCache(bundle){try{if(this.deps.cache){await this.deps.cache.write(bundle);return}const filePath=this.cachePath();await(0,import_utils.ensureDir)((0,import_utils.dirname)(filePath));await(0,import_utils.writeFile)(filePath,JSON.stringify(bundle))}catch(e){this.logger.warn(`[saas-source] cache persist failed: ${e.message}`)}}async loadCache(){try{if(this.deps.cache){const cached=await this.deps.cache.read();if(cached===void 0||cached===null)return void 0;return parseOverlay({kind:"object",content:cached})}const filePath=this.cachePath();const raw=await(0,import_utils.readFile)(filePath,"utf8");return parseOverlay({kind:"json",content:raw})}catch{return void 0}}notify(bundle){for(const fn of this.listeners){try{fn(bundle)}catch(e){this.logger.warn(`[saas-source] listener threw: ${e.message}`)}}}};var import_utils2=require("@frontmcp/utils");var StaticSource=class{constructor(options,logger){this.options=options;this.logger=logger;this.id=`static:${this.options.path}`}options;logger;id;listeners=new Set;watcher;debounceTimer;stopped=false;async start(){const bundle=await this.fetchOnce();this.notify(bundle);if(this.options.watch){this.beginWatch()}}onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async stop(){this.stopped=true;if(this.watcher){this.watcher.close();this.watcher=void 0}if(this.debounceTimer){clearTimeout(this.debounceTimer);this.debounceTimer=void 0}this.listeners.clear()}async fetchOnce(){const absPath=(0,import_utils2.pathResolve)(this.options.path);const raw=await(0,import_utils2.readFile)(absPath,"utf8");const ext=(0,import_utils2.extname)(absPath).toLowerCase();let input;if(ext===".json"){input={kind:"json",content:raw}}else if(ext===".yaml"||ext===".yml"){input={kind:"yaml",content:raw}}else{const trimmed=raw.trimStart();input=trimmed.startsWith("{")||trimmed.startsWith("[")?{kind:"json",content:raw}:{kind:"yaml",content:raw}}return parseOverlay(input)}notify(bundle){for(const fn of this.listeners){try{fn(bundle)}catch(e){this.logger.warn(`[static-source] listener threw: ${e.message}`)}}}beginWatch(){const absPath=(0,import_utils2.pathResolve)(this.options.path);try{this.watcher=(0,import_utils2.watchFile)(absPath,()=>this.scheduleRefresh())}catch(e){this.logger.warn(`[static-source] fs.watch failed (${e.message}); polling at 2s instead`);const tick=()=>{if(this.stopped)return;this.scheduleRefresh();setTimeout(tick,2e3).unref?.()};setTimeout(tick,2e3).unref?.()}}scheduleRefresh(){if(this.debounceTimer)clearTimeout(this.debounceTimer);this.debounceTimer=setTimeout(async()=>{if(this.stopped)return;try{const bundle=await this.fetchOnce();this.notify(bundle)}catch(e){this.logger.warn(`[static-source] refresh failed: ${e.message}`)}},250);this.debounceTimer.unref?.()}};function createBundleSource(source,cacheDir,logger,deps){switch(source.type){case"static":return new StaticSource(source,logger);case"npm":return new NpmSource(source,logger);case"saas":return new SaasPullSource(source,cacheDir,logger,deps);case"inline":return new InlineSource(source,logger)}}var import_mcp_from_openapi=require("mcp-from-openapi");async function compileSkilledBundleFromOpenApi(openapi,skills,options){const serviceId=options.serviceId??"api";const baseUrl=options.baseUrl??openapi.servers?.[0]?.url;if(!baseUrl){throw new Error("compileSkilledBundleFromOpenApi: no baseUrl \u2014 pass options.baseUrl or declare servers[] in the OpenAPI doc.")}const generator=await import_mcp_from_openapi.OpenAPIToolGenerator.fromJSON(openapi);const tools=await generator.generateTools();const byOperationId=new Map;for(const tool of tools){const opId=tool.metadata.operationId;if(opId)byOperationId.set(opId,tool)}const referenced=new Set;for(const skill of skills)for(const opId of skill.operationIds)referenced.add(opId);const operations={};for(const opId of referenced){const tool=byOperationId.get(opId);if(!tool){throw new Error(`compileSkilledBundleFromOpenApi: operationId "${opId}" is referenced by a skill but not defined in the OpenAPI document.`)}operations[opId]={operationId:opId,serviceId,httpMethod:tool.metadata.method.toUpperCase(),pathTemplate:tool.metadata.path,summary:tool.metadata.operationSummary??tool.description,inputSchema:tool.inputSchema,outputSchema:tool.outputSchema??{type:"object"},mapper:tool.mapper,authBindingRef:"default"}}return{schemaVersion:1,bundleId:options.bundleId,version:options.version,generatedAt:options.generatedAt??new Date().toISOString(),sourceDigest:options.sourceDigest??"0".repeat(64),services:[{id:serviceId,baseUrl}],authBindings:{default:options.authBinding??{kind:"none"}},skills:skills.map(s=>({id:s.id,name:s.name,description:s.description,instructions:s.instructions,tags:s.tags??[],operationIds:s.operationIds})),operations}}var import_utils3=require("@frontmcp/utils");var DEFAULT_OPTIONS={skillsDir:"./skills",watch:true,debounceMs:200,pollIntervalMs:2e3};var FilesystemSkillsSource=class{constructor(options,logger){this.logger=logger;this.options={...DEFAULT_OPTIONS,...options};this.id=`filesystem-skills:${this.options.skillsDir}`}logger;id;options;listeners=new Set;known=new Map;watcher;pollTimer;debounceTimers=new Map;stopped=false;rootResolved="";onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async start(){const absRoot=(0,import_utils3.pathResolve)(this.options.skillsDir);try{this.rootResolved=await(0,import_utils3.realpath)(absRoot)}catch{this.rootResolved=absRoot}await this.initialScan();if(this.options.watch){this.beginWatch()}}async stop(){this.stopped=true;if(this.watcher){try{this.watcher.close()}catch{}this.watcher=void 0}if(this.pollTimer){clearTimeout(this.pollTimer);this.pollTimer=void 0}for(const t of this.debounceTimers.values())clearTimeout(t);this.debounceTimers.clear();this.listeners.clear();this.known.clear()}async initialScan(){const entries=await this.listSkillSubdirs();for(const dir of entries){await this.refreshSkill(dir)}}async listSkillSubdirs(){let entries;try{entries=await(0,import_utils3.readdir)(this.rootResolved)}catch(e){this.logger.warn(`[fs-skills] cannot read ${this.rootResolved}: ${e.message}`);return[]}const out=[];for(const name of entries){if(name.startsWith("."))continue;const abs=(0,import_utils3.joinPath)(this.rootResolved,name);let resolved;try{resolved=await(0,import_utils3.realpath)(abs)}catch{continue}if(!resolved.startsWith(this.rootResolved+import_utils3.sep)&&resolved!==this.rootResolved){this.logger.warn(`[fs-skills] skipping ${name}: realpath escapes skillsDir`);continue}try{const st=await(0,import_utils3.stat)(resolved);if(st.isDirectory())out.push(resolved)}catch{}}return out.sort()}async refreshSkill(absDir){if(this.stopped)return;let exists=false;try{const st=await(0,import_utils3.stat)(absDir);exists=st.isDirectory()}catch{exists=false}if(!exists){const prev2=this.known.get(absDir);if(prev2){this.known.delete(absDir);this.emit({op:"delete",skillId:prev2.id,absPath:absDir})}return}let content;try{content=await loadFilesystemSkill(absDir)}catch(e){this.logger.warn(`[fs-skills] failed to load ${absDir}: ${e.message}`);return}const prev=this.known.get(absDir);if(prev&&skillContentEqual(prev,content)){return}this.known.set(absDir,content);this.emit({op:"upsert",skill:content,absPath:absDir})}emit(event){for(const fn of this.listeners){try{fn(event)}catch(e){this.logger.warn(`[fs-skills] listener threw: ${e.message}`)}}}beginWatch(){try{this.watcher=(0,import_utils3.watchFile)(this.rootResolved,(_eventType,filename)=>{if(this.stopped)return;if(!filename){this.scheduleRescan();return}const first=filename.split(import_utils3.sep)[0];if(!first||first.startsWith("."))return;const absDir=(0,import_utils3.joinPath)(this.rootResolved,first);this.scheduleRefresh(absDir)},{recursive:true});this.watcher.onError(e=>{this.logger.warn(`[fs-skills] fs.watch error: ${e.message}; falling back to polling`);this.watcher?.close();this.watcher=void 0;this.beginPolling()})}catch(e){this.logger.warn(`[fs-skills] fs.watch failed (${e.message}); polling at ${this.options.pollIntervalMs}ms instead`);this.beginPolling()}}beginPolling(){const tick=async()=>{if(this.stopped)return;try{await this.pollOnce()}catch(e){this.logger.warn(`[fs-skills] poll tick error: ${e.message}`)}if(this.stopped)return;this.pollTimer=setTimeout(tick,this.options.pollIntervalMs);this.pollTimer.unref?.()};this.pollTimer=setTimeout(tick,this.options.pollIntervalMs);this.pollTimer.unref?.()}async pollOnce(){const dirs=new Set(await this.listSkillSubdirs());for(const known of[...this.known.keys()]){if(!dirs.has(known)){await this.refreshSkill(known)}}for(const abs of dirs){await this.refreshSkill(abs)}}scheduleRescan(){const key="<rescan>";const existing=this.debounceTimers.get(key);if(existing)clearTimeout(existing);const t=setTimeout(()=>{this.debounceTimers.delete(key);void this.pollOnce().catch(e=>{this.logger.warn(`[fs-skills] rescan failed: ${e.message}`)})},this.options.debounceMs);t.unref?.();this.debounceTimers.set(key,t)}scheduleRefresh(absDir){const existing=this.debounceTimers.get(absDir);if(existing)clearTimeout(existing);const t=setTimeout(()=>{this.debounceTimers.delete(absDir);void this.refreshSkill(absDir).catch(e=>{this.logger.warn(`[fs-skills] refresh failed for ${absDir}: ${e.message}`)})},this.options.debounceMs);t.unref?.();this.debounceTimers.set(absDir,t)}};async function loadFilesystemSkill(absDir){const skillMdPath=(0,import_utils3.joinPath)(absDir,"SKILL.md");const raw=await(0,import_utils3.readFile)(skillMdPath,"utf8");const{frontmatter,body}=parseSkillFrontmatter(raw);const dirName=(0,import_utils3.basename)(absDir);const name=frontmatter["name"]??dirName;const description=frontmatter["description"]??"";if(!description){throw new Error(`SKILL.md missing 'description' frontmatter (in ${absDir})`)}const instructions=body.trim();if(!instructions){throw new Error(`SKILL.md missing body content (in ${absDir})`)}const tools=parseToolList(frontmatter["tools"]);const resources={};for(const sub of["scripts","references","examples","assets"]){const subAbs=(0,import_utils3.joinPath)(absDir,sub);try{const st=await(0,import_utils3.stat)(subAbs);if(st.isDirectory())resources[sub]=subAbs}catch{}}const out={id:name,name,description,instructions,tools,...Object.keys(resources).length>0&&{resources}};if(typeof frontmatter["license"]==="string")out.license=frontmatter["license"];if(typeof frontmatter["compatibility"]==="string")out.compatibility=frontmatter["compatibility"];return out}function parseSkillFrontmatter(raw){if(!raw.startsWith("---")){return{frontmatter:{},body:raw}}const closeIdx=raw.indexOf("\n---",3);if(closeIdx===-1){return{frontmatter:{},body:raw}}const yamlBlock=raw.slice(3,closeIdx).trim();let bodyStart=closeIdx+4;if(raw[bodyStart]==="\n")bodyStart+=1;const body=raw.slice(bodyStart);const fm={};let currentListKey;for(const line of yamlBlock.split("\n")){if(!line.trim())continue;if(line.startsWith("- ")&¤tListKey){const arr=fm[currentListKey]??[];arr.push(stripQuotes(line.slice(2).trim()));fm[currentListKey]=arr;continue}const colon=line.indexOf(":");if(colon===-1)continue;const key=line.slice(0,colon).trim();const rest=line.slice(colon+1).trim();if(!rest){currentListKey=key;fm[key]=[];continue}currentListKey=void 0;fm[key]=stripQuotes(rest)}return{frontmatter:fm,body}}function stripQuotes(s){if(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'")){return s.slice(1,-1)}return s}function parseToolList(raw){if(Array.isArray(raw)){return raw.map(t=>typeof t==="string"?{name:t}:null).filter(t=>t!==null)}if(typeof raw==="string"){return raw.split(",").map(t=>t.trim()).filter(Boolean).map(name=>({name}))}return[]}function skillContentEqual(a,b){return a.id===b.id&&a.name===b.name&&a.description===b.description&&a.instructions===b.instructions&&a.license===b.license&&a.compatibility===b.compatibility&&JSON.stringify(a.tools)===JSON.stringify(b.tools)&&JSON.stringify(a.resources??{})===JSON.stringify(b.resources??{})}var import_utils4=require("@frontmcp/utils");function classifyReason(reason){if(reason.includes("missing integrity"))return"missing_integrity";if(reason.includes("digest mismatch"))return"digest_mismatch";if(reason.includes("unknown signing keyId"))return"unknown_key_id";if(reason.includes("alg="))return"alg_mismatch";if(reason.includes("parse public key"))return"malformed_public_key";if(reason.includes("not valid base64url"))return"malformed_signature";if(reason.includes("signature verify threw"))return"verify_threw";if(reason.includes("signature verification failed"))return"verify_failed";return"other"}function canonicalize(value){if(value===null||typeof value!=="object"){return JSON.stringify(value)}if(Array.isArray(value)){return`[${value.map(v=>canonicalize(v)).join(",")}]`}const obj=value;const keys=Object.keys(obj).sort();return`{${keys.map(k=>`${JSON.stringify(k)}:${canonicalize(obj[k])}`).join(",")}}`}function bundleDigest(bundle){const{integrity:_drop,...rest}=bundle;void _drop;return(0,import_utils4.sha256Hex)(canonicalize(rest))}function decodeSignatureBytes(signatureBase64Url){try{return(0,import_utils4.base64urlDecode)(signatureBase64Url)}catch{return void 0}}function verifyBundleSignature(bundle,trustedKeys,telemetry){const failuresCounter=telemetry?.createCounter("frontmcp_skills_signature_failures_total","Number of bundle signature verification failures, partitioned by reason.");const verificationsCounter=telemetry?.createCounter("frontmcp_skills_signature_verifications_total","Number of bundle signature verification attempts, partitioned by status (ok|error).");const fail=reason=>{failuresCounter?.inc(1,{reason:classifyReason(reason)});verificationsCounter?.inc(1,{status:"error"});return{ok:false,reason}};if(!bundle.integrity){return fail("bundle missing integrity envelope")}const{alg,keyId,signature,digest}=bundle.integrity;const expectedDigest=bundleDigest(bundle);if(digest.toLowerCase()!==expectedDigest.toLowerCase()){return fail(`digest mismatch (envelope=${digest.slice(0,12)}.. computed=${expectedDigest.slice(0,12)}..)`)}const trusted=trustedKeys.find(k=>k.keyId===keyId);if(!trusted){return fail(`unknown signing keyId "${keyId}" \u2014 not in trustedKeys allowlist`)}if(trusted.alg!==alg){return fail(`key "${keyId}" alg=${trusted.alg} but envelope alg=${alg}`)}let publicJwk;try{publicJwk=(0,import_utils4.pemToPublicJwk)(trusted.publicKeyPem)}catch(e){return fail(`failed to parse public key for "${keyId}": ${e.message}`)}const sigBytes=decodeSignatureBytes(signature);if(!sigBytes){return fail("signature is not valid base64url")}const{integrity:_drop,...rest}=bundle;void _drop;const canonicalBytes=new TextEncoder().encode(canonicalize(rest));const verified=(0,import_utils4.rsaVerifySync)(alg,canonicalBytes,publicJwk,sigBytes);if(!verified){return fail("signature verification failed")}verificationsCounter?.inc(1,{status:"ok"});return{ok:true,keyId,alg}}var import_auth2=require("@frontmcp/auth");var DEFAULT_REQUIRED_ROLES=["frontmcp:cloud:push"];var BundlePushJwtVerifier=class{constructor(options,logger,jwks){this.options=options;this.logger=logger;this.jwks=jwks??new import_auth2.JwksService({});this.providerRef={id:"skilled-openapi-cloud-push",issuerUrl:options.expectedIssuer,jwksUri:options.jwksUri}}options;logger;providerRef;jwks;async verify(token){if(!token||typeof token!=="string"){return{ok:false,reason:"token missing"}}let res;try{res=await this.jwks.verifyTransparentToken(token,[this.providerRef])}catch(e){this.logger.debug(`[skilled-openapi] jwt verify threw: ${e.message}`);return{ok:false,reason:`verify failed: ${e.message}`}}if(!res.ok||!res.payload){return{ok:false,reason:res.error??"signature verify failed"}}const payload=res.payload;const aud=payload["aud"];const audValues=Array.isArray(aud)?aud:aud!==void 0?[aud]:[];if(!audValues.includes(this.options.expectedAudience)){return{ok:false,reason:`aud mismatch (expected "${this.options.expectedAudience}")`}}const resource=payload["resource"];const resValues=Array.isArray(resource)?resource:resource!==void 0?[resource]:[];if(!resValues.includes(this.options.expectedResource)){return{ok:false,reason:`RFC 8707 resource indicator mismatch (expected "${this.options.expectedResource}")`}}const requiredRoles=this.options.requiredRoles??DEFAULT_REQUIRED_ROLES;if(requiredRoles.length>0&&!this.matchesAny(payload["roles"],requiredRoles)){return{ok:false,reason:`missing required role (need one of ${requiredRoles.join(", ")})`}}if(this.options.requiredScopes&&this.options.requiredScopes.length>0){if(!this.matchesAny(payload["scope"]??payload["scp"],this.options.requiredScopes)){return{ok:false,reason:`missing required scope (need one of ${this.options.requiredScopes.join(", ")})`}}}return{ok:true,payload}}matchesAny(claim,required){const have=Array.isArray(claim)?claim.filter(v=>typeof v==="string"):typeof claim==="string"?claim.split(/\s+/).filter(Boolean):[];return required.some(r=>have.includes(r))}};var import_utils5=require("@frontmcp/utils");function classifyReplayReason(reason){if(reason.includes("not finite"))return"invalid_timestamp";if(reason.includes("too short"))return"invalid_nonce";if(reason.includes("outside"))return"outside_window";if(reason.includes("replay"))return"nonce_replay";return"other"}var DEFAULT_WINDOW_MS=5*60*1e3;var DEFAULT_CAPACITY=5e3;var WebhookReplayGuard=class{windowMs;capacity;seen=new Map;now=Date.now;rejectsCounter;checksCounter;constructor(options={}){const windowMs=options.windowMs??DEFAULT_WINDOW_MS;const capacity=options.capacity??DEFAULT_CAPACITY;if(!Number.isFinite(windowMs)||windowMs<=0){throw new RangeError("WebhookReplayGuard: windowMs must be a finite number > 0")}if(!Number.isInteger(capacity)||capacity<=0){throw new RangeError("WebhookReplayGuard: capacity must be a positive integer")}this.windowMs=windowMs;this.capacity=capacity;this.rejectsCounter=options.telemetry?.createCounter("frontmcp_skills_replay_rejects_total","Number of webhook replay-guard rejections, partitioned by reason.");this.checksCounter=options.telemetry?.createCounter("frontmcp_skills_replay_checks_total","Number of webhook replay-guard checks, partitioned by status (ok|error).")}setNowProvider(fn){this.now=fn}check(args){const{timestampMs,nonce}=args;const reject=reason=>{this.rejectsCounter?.inc(1,{reason:classifyReplayReason(reason)});this.checksCounter?.inc(1,{status:"error"});return{ok:false,reason}};if(!Number.isFinite(timestampMs)){return reject("timestamp not finite")}if(typeof nonce!=="string"||nonce.length<8){return reject("nonce too short")}const now=this.now();if(Math.abs(now-timestampMs)>this.windowMs){return reject(`timestamp outside ${this.windowMs}ms window`)}const key=(0,import_utils5.sha256Hex)(nonce);const expiresAt=this.seen.get(key);if(expiresAt!==void 0&&expiresAt>=now){return reject("nonce replay detected within freshness window")}if(this.seen.size>=this.capacity){this.prune()}if(this.seen.size>=this.capacity){let victimKey;let victimExpiry=Number.POSITIVE_INFINITY;for(const[candidateKey,candidateExpiry]of this.seen){if(candidateExpiry<victimExpiry){victimKey=candidateKey;victimExpiry=candidateExpiry}}if(victimKey!==void 0)this.seen.delete(victimKey)}this.seen.set(key,timestampMs+this.windowMs);this.checksCounter?.inc(1,{status:"ok"});return{ok:true}}prune(){const now=this.now();let removed=0;for(const[k,expiresAt]of this.seen){if(expiresAt<now){this.seen.delete(k);removed++}}return removed}get size(){return this.seen.size}};var SKILL_AUDIT_GENESIS_PREV_HASH="0".repeat(64);var SKILL_AUDIT_KEYS={sequence:"audit:skills:sequence",record:sequence=>`audit:skills:records:${sequence}`};var import_utils6=require("@frontmcp/utils");function canonicalizeRecordForSigning(record){const{signature:_drop,...rest}=record;void _drop;return canonicalize(rest)}function nextPrevHash(prev){if(!prev)return SKILL_AUDIT_GENESIS_PREV_HASH;return(0,import_utils6.sha256Hex)(canonicalizeRecordForSigning(prev))}function linkRecord(prev,partial){return{...partial,prevHash:nextPrevHash(prev)}}function verifyChain(records,trustedKeys,verifier,options={}){if(records.length===0){return{ok:true,verified:0}}let prev=options.previous;for(let i=0;i<records.length;i++){const rec=records[i];if(!rec){return{ok:false,breakAt:i,reason:"record at index is null/undefined"}}if(prev&&rec.sequence!==prev.sequence+1){return{ok:false,breakAt:rec.sequence,reason:`sequence gap: expected ${prev.sequence+1}, got ${rec.sequence}`}}const expectedPrev=nextPrevHash(prev);if(rec.prevHash!==expectedPrev){return{ok:false,breakAt:rec.sequence,reason:`prevHash mismatch at sequence ${rec.sequence}: expected ${expectedPrev.slice(0,12)}.. got ${rec.prevHash.slice(0,12)}..`}}const canonicalBytes=new TextEncoder().encode(canonicalizeRecordForSigning(rec));const ok=verifier({alg:rec.signatureAlg,keyId:rec.signatureKeyId,data:canonicalBytes,signatureBase64Url:rec.signature,trustedKeys:[...trustedKeys]});if(!ok){return{ok:false,breakAt:rec.sequence,reason:`signature verification failed at sequence ${rec.sequence} (keyId="${rec.signatureKeyId}", alg=${rec.signatureAlg})`}}prev=rec}return{ok:true,verified:records.length}}var import_utils7=require("@frontmcp/utils");function deriveFromKeyMaterial(ikm,info,length){return(0,import_utils7.hkdfSha256)(ikm,new Uint8Array(0),new TextEncoder().encode(info),length)}var Hs256AuditSigner=class{key;keyId;constructor(secret,keyId){if(!keyId){throw new Error("Hs256AuditSigner: keyId must be a non-empty string")}if(typeof secret==="string"){if(secret.length===0)throw new Error("Hs256AuditSigner: secret must be non-empty");this.key=new TextEncoder().encode(secret)}else{if(secret.length===0)throw new Error("Hs256AuditSigner: secret must be non-empty");this.key=new Uint8Array(secret)}this.keyId=keyId}sign(record){const canonicalBytes=new TextEncoder().encode(canonicalizeRecordForSigning({...record,signature:"",signatureKeyId:this.keyId,signatureAlg:"HS256"}));const mac=(0,import_utils7.hmacSha256)(this.key,canonicalBytes);return{signature:(0,import_utils7.base64urlEncode)(mac),keyId:this.keyId,alg:"HS256"}}getKeyId(){return this.keyId}getAlg(){return"HS256"}deriveKey(info,length){return deriveFromKeyMaterial(this.key,info,length)}};var Rs256AuditSigner=class{privateJwk;privateExponent;keyId;constructor(privateJwk,keyId){if(!keyId){throw new Error("Rs256AuditSigner: keyId must be a non-empty string")}if(!privateJwk||typeof privateJwk!=="object"){throw new Error("Rs256AuditSigner: privateJwk must be a JWK object")}if(privateJwk.kty!=="RSA"){throw new Error(`Rs256AuditSigner: privateJwk.kty must be "RSA", got "${String(privateJwk.kty)}"`)}if(!privateJwk.n||!privateJwk.e||!privateJwk.d){throw new Error("Rs256AuditSigner: privateJwk must include `n`, `e`, and `d` (RSA private key)")}this.privateJwk={...privateJwk};this.privateExponent=privateJwk.d;this.keyId=keyId}sign(record){const canonicalBytes=new TextEncoder().encode(canonicalizeRecordForSigning({...record,signature:"",signatureKeyId:this.keyId,signatureAlg:"RS256"}));const signature=(0,import_utils7.rsaSignBase64Url)("RS256",canonicalBytes,this.privateJwk);return{signature,keyId:this.keyId,alg:"RS256"}}getKeyId(){return this.keyId}getAlg(){return"RS256"}deriveKey(info,length){return deriveFromKeyMaterial((0,import_utils7.base64urlDecode)(this.privateExponent),info,length)}};var defaultAuditSignatureVerifier=({alg,keyId,data,signatureBase64Url,trustedKeys})=>{const trusted=trustedKeys.find(k=>k.keyId===keyId&&k.alg===alg);if(!trusted)return false;if(alg==="HS256"){if(!trusted.secret)return false;const expected=(0,import_utils7.hmacSha256)(trusted.secret,data);let actual;try{actual=(0,import_utils7.base64urlDecode)(signatureBase64Url)}catch{return false}if(actual.length!==expected.length)return false;return(0,import_utils7.timingSafeEqual)(actual,expected)}if(alg==="RS256"){if(!trusted.publicKeyPem&&!trusted.publicJwk)return false;let publicJwk;try{publicJwk=trusted.publicJwk??(0,import_utils7.pemToPublicJwk)(trusted.publicKeyPem)}catch{return false}let sigBytes;try{sigBytes=(0,import_utils7.base64urlDecode)(signatureBase64Url)}catch{return false}return(0,import_utils7.rsaVerifySync)("RS256",data,publicJwk,sigBytes)}const _exhaustive=alg;void _exhaustive;return false};var MemoryAuditStore=class{records=[];seq=0;async nextSequence(){this.seq+=1;return this.seq}async appendAtSequence(record){if(this.records.some(r=>r.sequence===record.sequence)){throw new Error(`MemoryAuditStore: record at sequence ${record.sequence} already exists`)}this.records.push(record)}async tail(){if(this.records.length===0)return void 0;return this.records[this.records.length-1]}async releaseSequence(sequence){if(sequence===this.seq&&!this.records.some(record=>record.sequence===sequence))this.seq-=1}async read(opts){const from=opts?.from??1;const limit=opts?.limit;const filtered=this.records.filter(r=>r.sequence>=from).sort((a,b)=>a.sequence-b.sequence);return limit!==void 0?filtered.slice(0,limit):filtered}};var StorageAdapterAuditStore=class{constructor(adapter,options={}){this.adapter=adapter;this.options=options}adapter;options;get sequenceKey(){return this.options.sequenceKey??SKILL_AUDIT_KEYS.sequence}recordKey(sequence){const prefix=this.options.recordKeyPrefix;return prefix?`${prefix}${sequence}`:SKILL_AUDIT_KEYS.record(sequence)}async nextSequence(){return this.adapter.incr(this.sequenceKey)}async releaseSequence(sequence){if(await this.adapter.get(this.recordKey(sequence))!==null)return;if(await this.adapter.decr(this.sequenceKey)!==sequence-1)await this.adapter.incr(this.sequenceKey)}async appendAtSequence(record){const key=this.recordKey(record.sequence);const existing=await this.adapter.get(key);if(existing!==null){throw new Error(`StorageAdapterAuditStore: record at sequence ${record.sequence} already exists`)}const payload=JSON.stringify(record);await this.adapter.set(key,payload,{ifNotExists:true});const stored=await this.adapter.get(key);if(stored!==payload){throw new Error(`StorageAdapterAuditStore: record at sequence ${record.sequence} was clobbered or not stored`)}}async tail(){const seqRaw=await this.adapter.get(this.sequenceKey);if(!seqRaw)return void 0;const seq=Number.parseInt(seqRaw,10);if(!Number.isFinite(seq)||seq<=0)return void 0;const maxLookback=this.options.tailLookback??1024;for(let s=seq;s>0&&s>seq-maxLookback;s--){const raw=await this.adapter.get(this.recordKey(s));if(!raw)continue;try{return JSON.parse(raw)}catch{throw new Error(`StorageAdapterAuditStore.tail: corrupt record at sequence ${s} cannot be parsed as JSON`)}}return void 0}async read(opts){const seqRaw=await this.adapter.get(this.sequenceKey);if(!seqRaw)return[];const head=Number.parseInt(seqRaw,10);if(!Number.isFinite(head)||head<=0)return[];const from=Math.max(1,opts?.from??1);const limit=opts?.limit;const out=[];for(let s=from;s<=head;s++){const raw=await this.adapter.get(this.recordKey(s));if(!raw)continue;try{const parsed=JSON.parse(raw);out.push(parsed);if(limit!==void 0&&out.length>=limit)break}catch{}}return out}};var import_utils8=require("@frontmcp/utils");function createSkillAuditMetrics(telemetry){const writeFailures=telemetry.createCounter("frontmcp_skills_audit_write_failures_total","Skill audit records that failed to sign or append, partitioned by reason.");const dropped=telemetry.createCounter("frontmcp_skills_audit_dropped_total","Skill audit records dropped before they were written, partitioned by reason.");return{incrementWriteFailure:reason=>writeFailures.inc(1,{reason}),incrementDropped:reason=>dropped.inc(1,{reason})}}var SKILL_AUDIT_ERROR_MESSAGE_MAX=500;var SKILL_AUDIT_QUEUE_MAX=1e3;var SUBJECT_HASH_KEY_INFO="frontmcp:audit:subject";var SUBJECT_HASH_KEY_LENGTH=32;var SkillAuditWriter=class{constructor(store,signer,logger,metrics,options={}){this.store=store;this.signer=signer;this.logger=logger;this.metrics=metrics;this.subjectMode=options.subjectMode??"hash";if(this.subjectMode==="hash"){this.subjectHashKey=options.subjectHashSecret??signer.deriveKey?.(SUBJECT_HASH_KEY_INFO,SUBJECT_HASH_KEY_LENGTH);if(!this.subjectHashKey){logger.warn(`[skill-audit] subjectMode 'hash' needs a key, and the signer (keyId "${signer.getKeyId()}") has no key material to derive one from, so subjects are recorded as 'redacted'. Set skillsConfig.audit.subjectHashSecret to record subject hashes.`)}}this.maxQueueDepth=options.maxQueueDepth??SKILL_AUDIT_QUEUE_MAX}store;signer;logger;metrics;chainHeadLock=Promise.resolve();queueDepth=0;subjectMode;subjectHashKey;maxQueueDepth;writeAuthorityPass(ctx){return this.write({phase:"authority-check-pass",ctx})}writeAuthorityFail(ctx,extras){return this.write({phase:"authority-check-fail",ctx,error:extras.reason})}writeHttpCallSuccess(ctx,extras){return this.write({phase:"http-call-success",ctx,status:extras.status,output:extras.output})}writeHttpCallFailure(ctx,extras){return this.write({phase:"http-call-failure",ctx,status:extras.status,error:extras.error})}async flush(){while(this.queueDepth>0){await this.chainHeadLock}}write(args){if(this.queueDepth>=this.maxQueueDepth){this.logger.warn(`[skill-audit] queue overflow (${this.queueDepth}/${this.maxQueueDepth}); dropping ${args.phase} record. Audit backend likely unhealthy.`);this.metrics?.incrementDropped?.("queue-overflow");return Promise.resolve()}this.queueDepth+=1;const next=this.chainHeadLock.then(()=>this.doWrite(args));this.chainHeadLock=next.catch(()=>void 0).finally(()=>{this.queueDepth-=1});return next.catch(()=>void 0)}async doWrite(args){const{phase,ctx,status,output,error}=args;let prev;try{prev=await this.store.tail()}catch(e){this.logger.warn(`[skill-audit] failed to load chain tail: ${e.message}`);this.metrics?.incrementWriteFailure("append");return}let inputHash;try{inputHash=(0,import_utils8.sha256Hex)(canonicalize(ctx.input??null))}catch(e){this.logger.warn(`[skill-audit] failed to canonicalize input: ${e.message}`);this.metrics?.incrementWriteFailure("unexpected");return}let sequence;try{sequence=await this.store.nextSequence()}catch(e){this.logger.warn(`[skill-audit] failed to allocate sequence: ${e.message}`);this.metrics?.incrementWriteFailure("append");return}const partial={id:(0,import_utils8.randomUUID)(),sequence,timestamp:new Date().toISOString(),subject:this.applySubjectMode(ctx.subject||"anonymous"),skillId:ctx.skillId,actionId:ctx.actionId,bundleId:ctx.bundleId,bundleVersion:ctx.bundleVersion,phase,inputHash,...status!==void 0?{status}:{},...phase==="http-call-success"?{outputHash:this.safeHash(output)}:{},...phase==="http-call-failure"||phase==="authority-check-fail"?{errorMessage:this.formatError(error)}:{}};const linked=linkRecord(prev,partial);let signed;try{const sig=this.signer.sign(linked);signed={...linked,signature:sig.signature,signatureKeyId:sig.keyId,signatureAlg:sig.alg}}catch(e){this.logger.warn(`[skill-audit] failed to sign record at seq=${sequence}: ${e.message}`);this.metrics?.incrementWriteFailure("sign");await this.releaseUnusedSequence(sequence);return}try{await this.store.appendAtSequence(signed);this.logger.debug?.(`[skill-audit] appended seq=${sequence} phase=${phase}`)}catch(e){this.logger.warn(`[skill-audit] failed to append record at seq=${sequence}: ${e.message}`);this.metrics?.incrementWriteFailure("append");await this.releaseUnusedSequence(sequence)}}async releaseUnusedSequence(allocated){if(!this.store.releaseSequence)return;try{await this.store.releaseSequence(allocated);this.logger.debug?.(`[skill-audit] rolled back unused sequence ${allocated}`)}catch{}}applySubjectMode(subject){if(this.subjectMode==="plain")return subject;if(this.subjectMode==="omit"||!this.subjectHashKey)return"redacted";const mac=(0,import_utils8.hmacSha256)(this.subjectHashKey,new TextEncoder().encode(subject));const hex=(0,import_utils8.bytesToHex)(mac);return`hashed:${hex.slice(0,32)}`}safeHash(value){try{return(0,import_utils8.sha256Hex)(canonicalize(value??null))}catch{return(0,import_utils8.sha256Hex)("audit:non-canonicalizable-output")}}formatError(error){let raw;if(error instanceof Error){raw=error.message}else if(typeof error==="string"){raw=error}else if(error===void 0||error===null){raw=""}else{try{raw=JSON.stringify(error)}catch{raw=String(error)}}if(raw.length>SKILL_AUDIT_ERROR_MESSAGE_MAX){return raw.slice(0,SKILL_AUDIT_ERROR_MESSAGE_MAX)+"..."}return raw}};var SkillAuditWriterToken=Symbol.for("frontmcp:SKILL_AUDIT_WRITER");var import_sdk=require("@frontmcp/sdk");var SPEC_ID_RE="[A-Za-z0-9][A-Za-z0-9_.-]*";var OPERATION_ID_RE="[A-Za-z_][A-Za-z0-9_]*";var TRAILING_BOUNDARY=String.raw`(?![A-Za-z0-9_\-])(?!\.[A-Za-z0-9_])`;var REFERENCE_RE=new RegExp(String.raw`(?<![A-Za-z0-9])op:\/\/(${SPEC_ID_RE})\/(${OPERATION_ID_RE})${TRAILING_BOUNDARY}`+`|`+String.raw`\[\[op:\s?(${SPEC_ID_RE})\/(${OPERATION_ID_RE})\]\]`,"g");function extractOpReferences(markdown){if(typeof markdown!=="string"||markdown.length===0)return[];const refs=[];const lineStarts=computeLineStarts(markdown);REFERENCE_RE.lastIndex=0;let match;while((match=REFERENCE_RE.exec(markdown))!==null){const offset=match.index;const[raw,uriSpec,uriOp,wikiSpec,wikiOp]=match;const isWikilink=wikiSpec!==void 0&&wikiOp!==void 0;const spec=isWikilink?wikiSpec:uriSpec;const operationId=isWikilink?wikiOp:uriOp;refs.push({spec,operationId,syntax:isWikilink?"wikilink":"uri",raw,location:offsetToLocation(offset,lineStarts)})}return refs}function buildKnownOps(entries){const map=new Map;for(const entry of entries){const slash=entry.indexOf("/");if(slash<=0||slash===entry.length-1)continue;const spec=entry.slice(0,slash);const op=entry.slice(slash+1);let set=map.get(spec);if(!set){set=new Set;map.set(spec,set)}set.add(op)}return map}function validateOpReferences(refs,knownOps){const diagnostics=[];for(const ref of refs){const specOps=knownOps.get(ref.spec);if(!specOps){diagnostics.push({ref,kind:"unknown-spec",message:`Unknown OpenAPI spec "${ref.spec}" referenced as ${ref.raw}`,suggestions:Array.from(knownOps.keys()).sort()});continue}if(!specOps.has(ref.operationId)){diagnostics.push({ref,kind:"unknown-operation",message:`Unknown operationId "${ref.operationId}" in spec "${ref.spec}" (${ref.raw})`,suggestions:suggestSimilarOps(ref.operationId,specOps)})}}return diagnostics}function dedupeOpReferences(refs){const seen=new Set;const result=[];for(const ref of refs){const key=`${ref.spec}/${ref.operationId}`;if(seen.has(key))continue;seen.add(key);result.push({spec:ref.spec,operationId:ref.operationId})}return result}function computeLineStarts(source){const starts=[0];for(let i=0;i<source.length;i++){if(source.charCodeAt(i)===10)starts.push(i+1)}return starts}function offsetToLocation(offset,lineStarts){let lo=0;let hi=lineStarts.length-1;while(lo<hi){const mid=lo+hi+1>>>1;if(lineStarts[mid]<=offset)lo=mid;else hi=mid-1}const line=lo+1;const column=offset-lineStarts[lo]+1;return{line,column,offset}}function suggestSimilarOps(query,known){if(known.size===0)return[];const q=query.toLowerCase();const candidates=Array.from(known);const prefix=candidates.filter(c=>c.toLowerCase().startsWith(q));const substring=candidates.filter(c=>!prefix.includes(c)&&c.toLowerCase().includes(q));const close=candidates.filter(c=>!prefix.includes(c)&&!substring.includes(c)).filter(c=>levenshtein(q,c.toLowerCase())<=Math.max(2,Math.floor(q.length/4)));return[...prefix,...substring,...close].slice(0,3)}function levenshtein(a,b){if(a===b)return 0;if(a.length===0)return b.length;if(b.length===0)return a.length;let prev=new Array(b.length+1);let curr=new Array(b.length+1);for(let j=0;j<=b.length;j++)prev[j]=j;for(let i=1;i<=a.length;i++){curr[0]=i;for(let j=1;j<=b.length;j++){const cost=a.charCodeAt(i-1)===b.charCodeAt(j-1)?0:1;curr[j]=Math.min(curr[j-1]+1,prev[j]+1,prev[j-1]+cost)}[prev,curr]=[curr,prev]}return prev[b.length]}var import_lazy_zod3=require("@frontmcp/lazy-zod");var IDENT_RE=/^[A-Za-z][A-Za-z0-9_-]*$/;var SPEC_ID_RE2=/^[A-Za-z0-9][A-Za-z0-9_.-]*$/;var SECRET_NAME_RE=/^[A-Z][A-Z0-9_]*$/;var BINDING_NAME_RE=/^[A-Z][A-Z0-9_]*$/;var ISO_DATE_RE=/^\d{4}-\d{2}-\d{2}$/;var serverInfoSchema=import_lazy_zod3.z.object({name:import_lazy_zod3.z.string().min(1).max(128),version:import_lazy_zod3.z.string().min(1).max(64),title:import_lazy_zod3.z.string().min(1).max(256).optional()}).strict();var serverSchema=import_lazy_zod3.z.object({info:serverInfoSchema,instructions:import_lazy_zod3.z.string().max(16*1024).optional()}).strict();var runtimeSchema=import_lazy_zod3.z.object({target:import_lazy_zod3.z.literal("cloudflare-worker"),compatibilityDate:import_lazy_zod3.z.string().regex(ISO_DATE_RE,"compatibilityDate must be ISO-8601 (YYYY-MM-DD)"),compatibilityFlags:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(128)).max(32).optional()}).strict();var specSourceSchema=import_lazy_zod3.z.union([import_lazy_zod3.z.string().min(1).max(2048),import_lazy_zod3.z.object({id:import_lazy_zod3.z.string().min(1).max(128).regex(SPEC_ID_RE2,"spec id must be a URI-safe identifier"),spec:import_lazy_zod3.z.string().min(1).max(2048),baseUrl:import_lazy_zod3.z.string().url("baseUrl must be a valid URL").optional(),bindingName:import_lazy_zod3.z.string().min(1).max(64).regex(IDENT_RE).optional()}).strict()]);var specsSchema=import_lazy_zod3.z.union([import_lazy_zod3.z.string().min(1).max(2048),import_lazy_zod3.z.array(specSourceSchema).max(64)]);var skillsSchema=import_lazy_zod3.z.object({source:import_lazy_zod3.z.string().min(1).max(2048).default("./skills/"),alwaysLoad:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(128)).max(32).optional(),tags:import_lazy_zod3.z.object({include:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(64)).max(64).optional(),exclude:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(64)).max(64).optional()}).strict().optional()}).strict();var tagSchema=import_lazy_zod3.z.object({name:import_lazy_zod3.z.string().min(1).max(64),description:import_lazy_zod3.z.string().min(1).max(512).optional()}).strict();var classificationRuleSchema=import_lazy_zod3.z.object({match:import_lazy_zod3.z.string().min(1).max(256),expose:import_lazy_zod3.z.enum(["tool","resource","both"]).optional(),emits:import_lazy_zod3.z.enum(["self","parent","none"]).optional()}).strict();var classificationSchema=import_lazy_zod3.z.object({rules:import_lazy_zod3.z.array(classificationRuleSchema).max(64).optional()}).strict();var durableObjectBindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),className:import_lazy_zod3.z.string().min(1).max(128).regex(IDENT_RE),scriptName:import_lazy_zod3.z.string().min(1).max(128).optional()}).strict();var d1BindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),databaseName:import_lazy_zod3.z.string().min(1).max(256),databaseId:import_lazy_zod3.z.string().min(1).max(128)}).strict();var kvBindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),id:import_lazy_zod3.z.string().min(1).max(128)}).strict();var r2BindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),bucketName:import_lazy_zod3.z.string().min(1).max(256)}).strict();var bindingsSchema=import_lazy_zod3.z.object({durableObjects:import_lazy_zod3.z.array(durableObjectBindingSchema).max(32).optional(),d1Databases:import_lazy_zod3.z.array(d1BindingSchema).max(16).optional(),kvNamespaces:import_lazy_zod3.z.array(kvBindingSchema).max(32).optional(),r2Buckets:import_lazy_zod3.z.array(r2BindingSchema).max(16).optional(),vars:import_lazy_zod3.z.record(import_lazy_zod3.z.string().min(1).max(128),import_lazy_zod3.z.string().max(4096)).optional()}).strict();var trustRootSchema=import_lazy_zod3.z.object({kid:import_lazy_zod3.z.string().min(1).max(128),publicKeySecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE)}).strict();var replayGuardSchema=import_lazy_zod3.z.object({windowSeconds:import_lazy_zod3.z.number().int().min(10).max(3600).default(300),nonceKv:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE)}).strict();var signingSchema=import_lazy_zod3.z.object({algorithm:import_lazy_zod3.z.enum(["ed25519","rs256"]),trustRoots:import_lazy_zod3.z.array(trustRootSchema).min(1).max(8),replay:replayGuardSchema}).strict();var fronteggAuthSchema=import_lazy_zod3.z.object({tenantResolver:import_lazy_zod3.z.enum(["subdomain","header","jwt-claim"]).default("subdomain"),audience:import_lazy_zod3.z.string().min(1).max(256),issuerSecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE)}).strict();var oauthAuthSchema=import_lazy_zod3.z.object({issuer:import_lazy_zod3.z.string().url(),audience:import_lazy_zod3.z.string().min(1).max(256),credentialsSecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE).optional()}).strict();var apiKeyAuthSchema=import_lazy_zod3.z.object({header:import_lazy_zod3.z.string().min(1).max(64).default("X-API-Key"),allowlistSecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE)}).strict();var authSchema=import_lazy_zod3.z.discriminatedUnion("provider",[import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("none")}).strict(),import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("frontegg"),frontegg:fronteggAuthSchema}).strict(),import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("oauth"),oauth:oauthAuthSchema}).strict(),import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("apiKey"),apiKey:apiKeyAuthSchema}).strict()]);var secretSchema=import_lazy_zod3.z.object({name:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE),required:import_lazy_zod3.z.boolean().default(true),description:import_lazy_zod3.z.string().min(1).max(512).optional()}).strict();var environmentOverlaySchema=import_lazy_zod3.z.object({server:serverSchema.partial().optional(),specs:specsSchema.optional(),skills:skillsSchema.partial().optional(),classification:classificationSchema.optional(),bindings:bindingsSchema.optional(),auth:authSchema.optional(),vars:import_lazy_zod3.z.record(import_lazy_zod3.z.string().min(1).max(128),import_lazy_zod3.z.string().max(4096)).optional()}).strict();var deployManifestSchema=import_lazy_zod3.z.object({$schema:import_lazy_zod3.z.string().url().optional(),version:import_lazy_zod3.z.literal(1),name:import_lazy_zod3.z.string().min(1).max(128).regex(IDENT_RE),runtime:runtimeSchema,server:serverSchema,specs:specsSchema,skills:skillsSchema.default({source:"./skills/"}),tags:import_lazy_zod3.z.array(tagSchema).max(64).optional(),classification:classificationSchema.optional(),bindings:bindingsSchema,signing:signingSchema,auth:authSchema,secrets:import_lazy_zod3.z.array(secretSchema).max(64).optional(),environments:import_lazy_zod3.z.record(import_lazy_zod3.z.string().min(1).max(64).regex(IDENT_RE),environmentOverlaySchema).optional()}).strict();function applyEnvironmentOverlay(base,overlay){const effectiveBindings=overlay.bindings??base.bindings;const mergedBindings=overlay.vars!==void 0?{...effectiveBindings,vars:{...effectiveBindings.vars??{},...overlay.vars}}:effectiveBindings;return{...base,server:overlay.server?{...base.server,...overlay.server}:base.server,specs:overlay.specs??base.specs,skills:overlay.skills?{...base.skills,...overlay.skills}:base.skills,classification:overlay.classification??base.classification,bindings:mergedBindings,auth:overlay.auth??base.auth}}function validateEffectiveManifest(manifest,label,errors){const declaredSecrets=new Set((manifest.secrets??[]).map(s=>s.name));const refs=[];if(manifest.auth.provider==="frontegg"){refs.push({where:"auth.frontegg.issuerSecret",name:manifest.auth.frontegg.issuerSecret})}else if(manifest.auth.provider==="oauth"&&manifest.auth.oauth.credentialsSecret){refs.push({where:"auth.oauth.credentialsSecret",name:manifest.auth.oauth.credentialsSecret})}else if(manifest.auth.provider==="apiKey"){refs.push({where:"auth.apiKey.allowlistSecret",name:manifest.auth.apiKey.allowlistSecret})}for(const root of manifest.signing.trustRoots){refs.push({where:`signing.trustRoots[kid=${root.kid}].publicKeySecret`,name:root.publicKeySecret})}for(const ref of refs){if(!declaredSecrets.has(ref.name)){errors.push(`${label}Secret "${ref.name}" referenced at ${ref.where} is not declared in secrets[]`)}}const declaredKv=new Set((manifest.bindings.kvNamespaces??[]).map(kv=>kv.binding));if(!declaredKv.has(manifest.signing.replay.nonceKv)){errors.push(`${label}signing.replay.nonceKv "${manifest.signing.replay.nonceKv}" does not match any bindings.kvNamespaces[].binding`)}for(const id of manifest.skills.alwaysLoad??[]){if(!/^[a-z0-9][a-z0-9-]*$/.test(id)){errors.push(`${label}skills.alwaysLoad entry "${id}" is not a valid kebab-case skill id`)}}if(manifest.tags){const declaredTags=new Set(manifest.tags.map(t=>t.name));const tagFilter=manifest.skills.tags;for(const t of[...tagFilter?.include??[],...tagFilter?.exclude??[]]){if(!declaredTags.has(t)){errors.push(`${label}skills.tags references unknown tag "${t}" (not in tags[])`)}}}}function crossValidateManifest(manifest){const errors=[];validateEffectiveManifest(manifest,"",errors);for(const[envName,overlay]of Object.entries(manifest.environments??{})){const effective=applyEnvironmentOverlay(manifest,overlay);validateEffectiveManifest(effective,`environments.${envName}: `,errors)}return errors.length===0?{ok:true}:{ok:false,errors}}function specStem(ref){const noQuery=ref.split("?")[0].split("#")[0];const base=noQuery.split("/").pop()??noQuery;const stem=base.replace(/\.(ya?ml|json)$/i,"");const safe=(stem||base).replace(/[^a-zA-Z0-9_.-]/g,"-");return safe||"spec"}function normalizeSpec(source){if(typeof source==="string"){const id=specStem(source);return{id,spec:source,bindingName:id}}return{id:source.id,spec:source.spec,baseUrl:source.baseUrl,bindingName:source.bindingName??source.id}}function buildFrontMcpConfigFromManifest(manifest){const info={name:manifest.server.info.name,version:manifest.server.info.version,...manifest.server.info.title!==void 0?{title:manifest.server.info.title}:{}};const skills={source:manifest.skills.source,...manifest.skills.alwaysLoad?{alwaysLoad:manifest.skills.alwaysLoad}:{},...manifest.skills.tags?{tags:manifest.skills.tags}:{}};let specs=[];let specsDir;if(typeof manifest.specs==="string"){specsDir=manifest.specs}else{specs=manifest.specs.map(normalizeSpec);const seen=new Set;for(const s of specs){if(seen.has(s.id)){throw new Error(`manifest specs: duplicate spec id "${s.id}" (derived from the filename stem). Give the colliding spec an explicit { id } in frontmcp.deploy.yaml.`)}seen.add(s.id)}}return{info,...manifest.server.instructions!==void 0?{instructions:manifest.server.instructions}:{},skillsConfig:{enabled:true,injectInstructions:"append"},skills,specs,...specsDir!==void 0?{specsDir}:{}}}function classifyOperations(specId,ops){if(!specId||specId.length===0){throw new Error("classifyOperations: specId is required")}if(!Array.isArray(ops))return[];const pathsWithGet=new Set;for(const op of ops){if(op&&typeof op.path==="string"&&typeof op.method==="string"&&op.method.toUpperCase()==="GET"){pathsWithGet.add(op.path)}}return ops.map(op=>classifyOne(specId,op,pathsWithGet))}function classifyOne(specId,op,pathsWithGet){const method=normaliseMethod(op.method);const path=op.path;const hasTerminalParam=pathHasTerminalParam(path);const parent=parentPath(path);const thisIsResource=pathsWithGet.has(path);const parentIsResource=parent!==null&&pathsWithGet.has(parent);const selfUri=pathToResourceUri(specId,path);const parentUri=parent!==null?pathToResourceUri(specId,parent):null;if(method==="GET"){if(hasTerminalParam){return{operationId:op.operationId,method,path,specId,expose:"both",resourceUriTemplate:selfUri}}return{operationId:op.operationId,method,path,specId,expose:"resource",resourceUriTemplate:selfUri}}const base={operationId:op.operationId,method,path,specId,expose:"tool"};if(method==="POST"){if(thisIsResource&&!hasTerminalParam){return{...base,emit:{kind:"listChanged",pathTemplate:path,resourceUriTemplate:selfUri}}}if(thisIsResource&&hasTerminalParam){return{...base,emit:{kind:"updated",pathTemplate:path,resourceUriTemplate:selfUri}}}if(parentIsResource&&parentUri!==null&&parent!==null){return{...base,emit:{kind:"updated",pathTemplate:parent,resourceUriTemplate:parentUri}}}return base}if(method==="PUT"||method==="PATCH"){if(thisIsResource){return{...base,emit:{kind:"updated",pathTemplate:path,resourceUriTemplate:selfUri}}}if(parentIsResource&&parentUri!==null&&parent!==null){return{...base,emit:{kind:"updated",pathTemplate:parent,resourceUriTemplate:parentUri}}}return base}if(method==="DELETE"){if(hasTerminalParam&&parentIsResource&&parentUri!==null&&parent!==null){return{...base,emit:{kind:"listChanged",pathTemplate:parent,resourceUriTemplate:parentUri}}}if(!hasTerminalParam&&thisIsResource){return{...base,emit:{kind:"listChanged",pathTemplate:path,resourceUriTemplate:selfUri}}}return base}return base}function applyClassificationOverrides(classified,rules){if(!Array.isArray(rules)||rules.length===0)return classified.slice();const compiled=rules.map(rule=>({rule,matcher:compileMatcher(rule.match)}));return classified.map(op=>{for(const{rule,matcher}of compiled){if(!matcher(op))continue;return applyOne(op,rule)}return op})}var KNOWN_METHODS=new Set(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS","TRACE"]);function normaliseMethod(m){const up=(m??"").toUpperCase();return KNOWN_METHODS.has(up)?up:up}function pathHasTerminalParam(path){if(typeof path!=="string"||path.length===0)return false;const segments=path.split("/").filter(s=>s.length>0);if(segments.length===0)return false;const last=segments[segments.length-1];return last.startsWith("{")&&last.endsWith("}")&&last.length>=3}function parentPath(path){if(typeof path!=="string"||path.length===0)return null;const segments=path.split("/").filter(s=>s.length>0);if(segments.length<=1)return null;return"/"+segments.slice(0,-1).join("/")}function pathToResourceUri(specId,path){const cleaned=path.startsWith("/")?path.slice(1):path;return`mcp+op://${specId}/${cleaned}`}function compileMatcher(match){const trimmed=(match??"").trim();const sep2=trimmed.indexOf(" ");if(sep2<=0||sep2===trimmed.length-1){return()=>false}const methodPattern=trimmed.slice(0,sep2).toUpperCase();const pathPattern=trimmed.slice(sep2+1);const pathRe=globToRegExp(pathPattern);return op=>{if(methodPattern!=="*"&&methodPattern!==op.method)return false;return pathRe.test(op.path)}}function globToRegExp(glob){const STAR_STAR="\0";const STAR="";const replaced=glob.replace(/\*\*/g,STAR_STAR).replace(/\*/g,STAR);const escaped=replaced.replace(/[.+^${}()|[\]\\]/g,"\\$&");const expanded=escaped.split(STAR_STAR).join(".*").split(STAR).join("[^/]*");return new RegExp(`^${expanded}$`)}function defaultEmitKindForMethod(method){return method==="DELETE"?"listChanged":"updated"}function applyOne(op,rule){let next={...op};if(rule.expose!==void 0){next={...next,expose:rule.expose}}if(rule.emits!==void 0){if(rule.emits==="none"){const{emit:_drop,...rest}=next;next=rest}else if(rule.emits==="self"){const target=pathToResourceUri(op.specId,op.path);next={...next,emit:{kind:next.emit?.kind??defaultEmitKindForMethod(op.method),pathTemplate:op.path,resourceUriTemplate:target}}}else if(rule.emits==="parent"){const parent=parentPath(op.path);if(parent!==null){next={...next,emit:{kind:next.emit?.kind??defaultEmitKindForMethod(op.method),pathTemplate:parent,resourceUriTemplate:pathToResourceUri(op.specId,parent)}}}else{const{emit:_drop,...rest}=next;next=rest}}}return next}var TEMPLATE_PLACEHOLDER_RE=/\{([^{}]+)\}/g;function renderResourceUri(template,args){if(typeof template!=="string"||template.length===0){return{ok:false,missing:[]}}const argObj=args!==null&&typeof args==="object"?args:{};const missing=[];const uri=template.replace(TEMPLATE_PLACEHOLDER_RE,(_match,name)=>{const value=argObj[name];if(value===void 0||value===null){missing.push(name);return""}if(typeof value==="object"){missing.push(name);return""}if(typeof value==="number"&&!Number.isFinite(value)){missing.push(name);return""}return encodeURIComponent(String(value))});if(missing.length>0)return{ok:false,missing};return{ok:true,uri}}function buildResourceChangeNotification(classification,args){const emit=classification.emit;if(!emit)return{notification:null,reason:"no-emit"};if(emit.kind==="listChanged"){const rendered2=renderResourceUri(emit.resourceUriTemplate,args);if(!rendered2.ok){return{notification:null,reason:"unresolved-template",missing:rendered2.missing}}return{notification:{method:"notifications/resources/list_changed"}}}const rendered=renderResourceUri(emit.resourceUriTemplate,args);if(!rendered.ok){return{notification:null,reason:"unresolved-template",missing:rendered.missing}}return{notification:{method:"notifications/resources/updated",params:{uri:rendered.uri}}}}var ClassificationRegistry=class{byToolName=new Map;register(toolName,classification){if(typeof toolName!=="string"||toolName.length===0){throw new Error("ClassificationRegistry.register: toolName must be a non-empty string")}const prev=this.byToolName.get(toolName);this.byToolName.set(toolName,classification);return prev}registerAll(classifications){let added=0;let replaced=0;for(const c of classifications){const toolName=`${c.specId}.${c.operationId}`;const prev=this.byToolName.get(toolName);this.byToolName.set(toolName,c);if(prev)replaced++;else added++}return{added,replaced}}lookup(toolName){return this.byToolName.get(toolName)}unregister(toolName){return this.byToolName.delete(toolName)}clear(){this.byToolName.clear()}size(){return this.byToolName.size}getAll(){const out=[];for(const[toolName,classification]of this.byToolName.entries()){out.push({toolName,classification})}return out}buildNotificationForCall(toolName,args){const classification=this.byToolName.get(toolName);if(!classification)return{notification:null,reason:"no-emit"};return buildResourceChangeNotification(classification,args)}};0&&(module.exports={BundlePinnedError,BundlePushJwtVerifier,BundleStore,ClassificationRegistry,FilesystemSkillsSource,Hs256AuditSigner,InlineSource,MemoryAuditStore,NpmSource,OverlayParseError,Rs256AuditSigner,SKILL_AUDIT_ERROR_MESSAGE_MAX,SKILL_AUDIT_GENESIS_PREV_HASH,SKILL_AUDIT_KEYS,SKILL_AUDIT_QUEUE_MAX,SaasPullSource,SkillAuditWriter,SkillAuditWriterToken,SkillDependencyCycleError,SkillDependencyInvariantError,SkillDependencyMissingError,StaticSource,StorageAdapterAuditStore,WebhookReplayGuard,applyClassificationOverrides,applyEnvironmentOverlay,buildFrontMcpConfigFromManifest,buildKnownOps,buildResourceChangeNotification,bundleDigest,bundleSkillToActions,bundleSourceSchema,canonicalize,canonicalizeRecordForSigning,classifyOne,classifyOperations,compileSkilledBundleFromOpenApi,createBundleSource,createSkillAuditMetrics,crossValidate,crossValidateManifest,dedupeOpReferences,defaultAuditSignatureVerifier,deployManifestSchema,diffBundles,extractOpReferences,formatDiffSummary,hasSkillAuditFactory,linkRecord,loadFilesystemSkill,nextPrevHash,npmSourceSchema,parseOverlay,parseSkillFrontmatter,renderResourceUri,resolveSkillLoadOrder,resolvedBundleSchema,saasSourceSchema,setSkillAuditFactory,signatureKeySchema,staticSourceSchema,validateOpReferences,verifyBundleSignature,verifyChain});
|
|
1
|
+
"use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:true}):target,mod));var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var skills_exports={};__export(skills_exports,{BundlePinnedError:()=>BundlePinnedError,BundlePushJwtVerifier:()=>BundlePushJwtVerifier,BundleStore:()=>BundleStore,ClassificationRegistry:()=>ClassificationRegistry,FilesystemSkillsSource:()=>FilesystemSkillsSource,Hs256AuditSigner:()=>Hs256AuditSigner,InlineSource:()=>InlineSource,MemoryAuditStore:()=>MemoryAuditStore,NpmSource:()=>NpmSource,OverlayParseError:()=>OverlayParseError,Rs256AuditSigner:()=>Rs256AuditSigner,SKILL_AUDIT_ERROR_MESSAGE_MAX:()=>SKILL_AUDIT_ERROR_MESSAGE_MAX,SKILL_AUDIT_GENESIS_PREV_HASH:()=>SKILL_AUDIT_GENESIS_PREV_HASH,SKILL_AUDIT_KEYS:()=>SKILL_AUDIT_KEYS,SKILL_AUDIT_QUEUE_MAX:()=>SKILL_AUDIT_QUEUE_MAX,SaasPullSource:()=>SaasPullSource,SaasPushRefusedError:()=>SaasPushRefusedError,SkillAuditWriter:()=>SkillAuditWriter,SkillAuditWriterToken:()=>SkillAuditWriterToken,SkillDependencyCycleError:()=>SkillDependencyCycleError,SkillDependencyInvariantError:()=>SkillDependencyInvariantError,SkillDependencyMissingError:()=>SkillDependencyMissingError,StaticSource:()=>StaticSource,StorageAdapterAuditStore:()=>StorageAdapterAuditStore,WebhookReplayGuard:()=>WebhookReplayGuard,applyClassificationOverrides:()=>applyClassificationOverrides,applyEnvironmentOverlay:()=>applyEnvironmentOverlay,buildFrontMcpConfigFromManifest:()=>buildFrontMcpConfigFromManifest,buildKnownOps:()=>buildKnownOps,buildResourceChangeNotification:()=>buildResourceChangeNotification,bundleDigest:()=>bundleDigest,bundleSkillToActions:()=>bundleSkillToActions,bundleSourceSchema:()=>bundleSourceSchema,canonicalize:()=>canonicalize,canonicalizeRecordForSigning:()=>canonicalizeRecordForSigning,classifyOne:()=>classifyOne,classifyOperations:()=>classifyOperations,compileSkilledBundleFromOpenApi:()=>compileSkilledBundleFromOpenApi,createBundleSource:()=>createBundleSource,createSkillAuditMetrics:()=>createSkillAuditMetrics,crossValidate:()=>crossValidate,crossValidateManifest:()=>crossValidateManifest,dedupeOpReferences:()=>dedupeOpReferences,defaultAuditSignatureVerifier:()=>defaultAuditSignatureVerifier,deployManifestSchema:()=>deployManifestSchema,diffBundles:()=>diffBundles,extractOpReferences:()=>extractOpReferences,formatDiffSummary:()=>formatDiffSummary,hasSkillAuditFactory:()=>import_sdk.hasSkillAuditFactory,linkRecord:()=>linkRecord,loadFilesystemSkill:()=>loadFilesystemSkill,nextPrevHash:()=>nextPrevHash,npmSourceSchema:()=>npmSourceSchema,parseOverlay:()=>parseOverlay,parseSkillFrontmatter:()=>parseSkillFrontmatter,renderResourceUri:()=>renderResourceUri,resolveSkillLoadOrder:()=>resolveSkillLoadOrder,resolvedBundleSchema:()=>resolvedBundleSchema,saasSourceSchema:()=>saasSourceSchema,setSkillAuditFactory:()=>import_sdk.setSkillAuditFactory,signatureKeySchema:()=>signatureKeySchema,staticSourceSchema:()=>staticSourceSchema,validateOpReferences:()=>validateOpReferences,verifyBundleSignature:()=>verifyBundleSignature,verifyChain:()=>verifyChain});module.exports=__toCommonJS(skills_exports);function bundleSkillToActions(skill,ops){return skill.operationIds.map(opId=>{const op=ops[opId];if(!op){throw new Error(`bundleSkillToActions: skill "${skill.id}" references unknown operationId "${opId}"`)}const action={actionId:op.operationId,summary:op.summary??`${op.httpMethod} ${op.pathTemplate}`,...op.description!==void 0&&{description:op.description},inputJsonSchema:op.inputSchema,outputJsonSchema:op.outputSchema,...op.requiredAuthorities!==void 0&&{requiredAuthorities:op.requiredAuthorities}};return action})}var import_lazy_zod=require("@frontmcp/lazy-zod");var httpMethodSchema=import_lazy_zod.z.enum(["GET","POST","PUT","PATCH","DELETE","HEAD"]);var RFC7230_TOKEN=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;var pathTemplateRegex=/^\/[^\s?#]*$/;var FORBIDDEN_PATH_FRAGMENTS=["..","\\","`","$(","${"];var pathTemplateSchema=import_lazy_zod.z.string().min(1).max(2048).refine(v=>pathTemplateRegex.test(v),{message:"pathTemplate must start with `/` and contain no whitespace, `?`, or `#`"}).refine(v=>!FORBIDDEN_PATH_FRAGMENTS.some(bad=>v.includes(bad)),{message:"pathTemplate contains a forbidden character or fragment (`..`, backtick, `$(`, etc.)"});var jsonSchemaShape=import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown());var authoritiesPolicySchema=import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown());var authBindingSchema=import_lazy_zod.z.discriminatedUnion("kind",[import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("none")}).strict(),import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("bearer"),vaultRef:import_lazy_zod.z.string().min(1).max(256),passthroughCallerToken:import_lazy_zod.z.boolean().optional()}).strict(),import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("apiKey"),in:import_lazy_zod.z.enum(["header","query"]),name:import_lazy_zod.z.string().min(1).max(128).regex(RFC7230_TOKEN,"apiKey name must match RFC 7230 token grammar"),vaultRef:import_lazy_zod.z.string().min(1).max(256)}).strict(),import_lazy_zod.z.object({kind:import_lazy_zod.z.literal("oauth2"),flow:import_lazy_zod.z.literal("client_credentials"),vaultRef:import_lazy_zod.z.string().min(1).max(256)}).strict()]);var serviceDescriptorSchema=import_lazy_zod.z.object({id:import_lazy_zod.z.string().min(1).max(128).regex(/^[a-zA-Z0-9_-]+$/,"service id must be alphanumeric / `-` / `_`"),baseUrl:import_lazy_zod.z.string().url().max(2048),description:import_lazy_zod.z.string().max(4096).optional()}).strict();var parameterMapperSchema=import_lazy_zod.z.object({inputKey:import_lazy_zod.z.string().min(1).max(256),type:import_lazy_zod.z.enum(["path","query","header","cookie","body"]),key:import_lazy_zod.z.string().min(1).max(256),required:import_lazy_zod.z.boolean().optional(),style:import_lazy_zod.z.string().max(64).optional(),explode:import_lazy_zod.z.boolean().optional(),serialization:import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown()).optional(),security:import_lazy_zod.z.record(import_lazy_zod.z.string(),import_lazy_zod.z.unknown()).optional()}).passthrough().refine(mapper=>mapper.type!=="header"&&mapper.type!=="cookie"||RFC7230_TOKEN.test(mapper.key),{message:"a header or cookie mapper key must match RFC 7230 token grammar",path:["key"]});var operationDescriptorSchema=import_lazy_zod.z.object({operationId:import_lazy_zod.z.string().min(1).max(256).regex(/^[a-zA-Z0-9_.\-:]+$/,"operationId must be alphanumeric / `-` / `_` / `.` / `:`"),serviceId:import_lazy_zod.z.string().min(1).max(128),httpMethod:httpMethodSchema,pathTemplate:pathTemplateSchema,inputSchema:jsonSchemaShape,outputSchema:jsonSchemaShape,mapper:import_lazy_zod.z.array(parameterMapperSchema).max(256),authBindingRef:import_lazy_zod.z.string().min(1).max(128),requiredAuthorities:authoritiesPolicySchema.optional(),public:import_lazy_zod.z.boolean().optional(),maxResponseBytes:import_lazy_zod.z.number().int().positive().max(64*1024*1024).optional(),timeoutMs:import_lazy_zod.z.number().int().positive().max(6e5).optional(),summary:import_lazy_zod.z.string().max(512).optional(),description:import_lazy_zod.z.string().max(8192).optional(),tags:import_lazy_zod.z.array(import_lazy_zod.z.string().max(256)).max(64).optional()}).strict();var bundledSkillSchema=import_lazy_zod.z.object({id:import_lazy_zod.z.string().min(1).max(128).regex(/^[a-zA-Z0-9_.-]+$/),name:import_lazy_zod.z.string().min(1).max(256),description:import_lazy_zod.z.string().max(2048),instructions:import_lazy_zod.z.string().max(256*1024),tags:import_lazy_zod.z.array(import_lazy_zod.z.string().max(64)).max(64).optional(),operationIds:import_lazy_zod.z.array(import_lazy_zod.z.string().min(1).max(256)).min(0).max(2e3),requiredAuthorities:authoritiesPolicySchema.optional(),requires:import_lazy_zod.z.array(import_lazy_zod.z.string().min(1).max(128)).max(64).optional()}).strict();var integritySchema=import_lazy_zod.z.object({alg:import_lazy_zod.z.enum(["RS256","EdDSA"]),keyId:import_lazy_zod.z.string().min(1).max(256),signature:import_lazy_zod.z.string().min(1).max(8192),digest:import_lazy_zod.z.string().min(1).max(256)}).strict();var resolvedBundleSchema=import_lazy_zod.z.object({schemaVersion:import_lazy_zod.z.literal(1),bundleId:import_lazy_zod.z.string().min(1).max(256).regex(/^[a-zA-Z0-9_.\-:]+$/),version:import_lazy_zod.z.string().min(1).max(256),generatedAt:import_lazy_zod.z.string().min(1).refine(v=>!Number.isNaN(Date.parse(v)),{message:"generatedAt must be ISO 8601 parsable"}),sourceDigest:import_lazy_zod.z.string().min(1).max(256).regex(/^[a-fA-F0-9]+$/,"sourceDigest must be a hex sha256"),services:import_lazy_zod.z.array(serviceDescriptorSchema).min(1).max(64),authBindings:import_lazy_zod.z.record(import_lazy_zod.z.string().min(1).max(128),authBindingSchema),skills:import_lazy_zod.z.array(bundledSkillSchema).max(1e3),operations:import_lazy_zod.z.record(import_lazy_zod.z.string().min(1).max(256),operationDescriptorSchema),integrity:integritySchema.optional()}).strict();function crossValidate(bundle){const errors=[];const serviceIds=new Set(bundle.services.map(s=>s.id));const authRefs=new Set(Object.keys(bundle.authBindings));const opIds=new Set(Object.keys(bundle.operations));for(const[opId,op]of Object.entries(bundle.operations)){if(op.operationId!==opId){errors.push(`operation key "${opId}" does not match operationId "${op.operationId}"`)}if(!serviceIds.has(op.serviceId)){errors.push(`operation "${opId}" references unknown service "${op.serviceId}"`)}if(!authRefs.has(op.authBindingRef)){errors.push(`operation "${opId}" references unknown authBinding "${op.authBindingRef}"`)}}const seenSkillIds=new Set;for(const skill of bundle.skills){if(seenSkillIds.has(skill.id)){errors.push(`duplicate skill id "${skill.id}"`)}seenSkillIds.add(skill.id);for(const opId of skill.operationIds){if(!opIds.has(opId)){errors.push(`skill "${skill.id}" references unknown operationId "${opId}"`)}}}return errors.length===0?{ok:true}:{ok:false,errors}}var yaml=__toESM(require("js-yaml"));var OVERLAY_BUNDLE_KEY="x-frontmcp-bundle";var OverlayParseError=class extends Error{constructor(message,errors){super(message);this.errors=errors;this.name="OverlayParseError"}errors};function parseRaw(input){switch(input.kind){case"yaml":try{return yaml.load(input.content,{schema:yaml.CORE_SCHEMA})}catch(e){throw new OverlayParseError(`Overlay YAML parse failed: ${e.message}`)}case"json":try{return JSON.parse(input.content)}catch(e){throw new OverlayParseError(`Overlay JSON parse failed: ${e.message}`)}case"object":return input.content}}function extractBundle(overlay){if(overlay===null||typeof overlay!=="object"){throw new OverlayParseError("Overlay root must be an object")}const root=overlay;if(OVERLAY_BUNDLE_KEY in root){return root[OVERLAY_BUNDLE_KEY]}const info=root["info"];if(info&&typeof info==="object"&&OVERLAY_BUNDLE_KEY in info){return info[OVERLAY_BUNDLE_KEY]}if("schemaVersion"in root&&"bundleId"in root){return root}throw new OverlayParseError("Overlay does not contain `info.x-frontmcp-bundle` or a bare bundle object with schemaVersion + bundleId")}function parseOverlay(input){const raw=parseRaw(input);const bundleCandidate=extractBundle(raw);const result=resolvedBundleSchema.safeParse(bundleCandidate);if(!result.success){const errors=result.error.issues.map(i=>`${i.path.join(".")||"<root>"}: ${i.message}`);throw new OverlayParseError(`Bundle schema validation failed (${errors.length} issue(s))`,errors)}const cross=crossValidate(result.data);if(!cross.ok){throw new OverlayParseError(`Bundle cross-reference validation failed (${cross.errors.length} issue(s))`,cross.errors)}return result.data}var opEqual=(a,b)=>a.operationId===b.operationId&&a.serviceId===b.serviceId&&a.httpMethod===b.httpMethod&&a.pathTemplate===b.pathTemplate&&a.authBindingRef===b.authBindingRef&&JSON.stringify(a.mapper??null)===JSON.stringify(b.mapper??null)&&JSON.stringify(a.inputSchema)===JSON.stringify(b.inputSchema)&&JSON.stringify(a.outputSchema)===JSON.stringify(b.outputSchema)&&JSON.stringify(a.requiredAuthorities??null)===JSON.stringify(b.requiredAuthorities??null)&&(a.public??false)===(b.public??false)&&a.maxResponseBytes===b.maxResponseBytes&&a.timeoutMs===b.timeoutMs&&JSON.stringify(a.tags??[])===JSON.stringify(b.tags??[]);var skillEqual=(a,b)=>a.id===b.id&&a.name===b.name&&a.description===b.description&&a.instructions===b.instructions&&JSON.stringify(a.tags??[])===JSON.stringify(b.tags??[])&&JSON.stringify(a.operationIds)===JSON.stringify(b.operationIds)&&JSON.stringify(a.requiredAuthorities??null)===JSON.stringify(b.requiredAuthorities??null);function diffBundles(oldBundle,newBundle){const oldSkills=new Map((oldBundle?.skills??[]).map(s=>[s.id,s]));const newSkills=new Map(newBundle.skills.map(s=>[s.id,s]));const addedSkillIds=[];const removedSkillIds=[];const changedSkillIds=[];for(const[id,skill]of newSkills){const prev=oldSkills.get(id);if(!prev)addedSkillIds.push(id);else if(!skillEqual(prev,skill))changedSkillIds.push(id)}for(const id of oldSkills.keys()){if(!newSkills.has(id))removedSkillIds.push(id)}const oldOps=oldBundle?.operations??{};const newOps=newBundle.operations;const addedOperationIds=[];const removedOperationIds=[];const changedOperationIds=[];for(const[id,op]of Object.entries(newOps)){const prev=oldOps[id];if(!prev)addedOperationIds.push(id);else if(!opEqual(prev,op))changedOperationIds.push(id)}for(const id of Object.keys(oldOps)){if(!(id in newOps))removedOperationIds.push(id)}const isNoOp=!!oldBundle&&oldBundle.version===newBundle.version&&addedSkillIds.length===0&&removedSkillIds.length===0&&changedSkillIds.length===0&&addedOperationIds.length===0&&removedOperationIds.length===0&&changedOperationIds.length===0;return{addedSkillIds,removedSkillIds,changedSkillIds,addedOperationIds,removedOperationIds,changedOperationIds,isNoOp}}function formatDiffSummary(diff){if(diff.isNoOp)return"no-op";return[`+${diff.addedSkillIds.length}/-${diff.removedSkillIds.length}/~${diff.changedSkillIds.length} skills`,`+${diff.addedOperationIds.length}/-${diff.removedOperationIds.length}/~${diff.changedOperationIds.length} ops`].join(", ")}var KNOWN_BUNDLE_SOURCES=["static","npm","saas-pull","webhook","filesystem","unknown"];function normalizeBundleSource(value){if(typeof value!=="string")return"unknown";return KNOWN_BUNDLE_SOURCES.includes(value)?value:"unknown"}function classifySwapError(err){if(err instanceof BundlePinnedError)return"pinned";if(err instanceof Error){const name=err.name;const msg=err.message.toLowerCase();if(name==="BundlePinnedError")return"pinned";if(msg.includes("bundle is required")||msg.includes("malformed"))return"invalid_bundle";if(msg.includes("listener"))return"listener_error"}return"unknown"}var BundleStore=class{active;listeners=new Set;history=[];pinnedVersion;historySize;telemetry;pullsCounter;constructor(options={}){const requested=options.historySize??3;if(!Number.isInteger(requested)||requested<1){throw new Error("BundleStore: historySize must be a positive integer")}this.historySize=requested;this.telemetry=options.telemetry;this.pullsCounter=this.telemetry?.createCounter("frontmcp_skills_bundle_pulls_total","Number of bundle pulls (swaps) attempted, partitioned by status / source.")}current(){return this.active}swap(next,source){if(!next){throw new Error("BundleStore.swap: bundle is required")}const sourceLabel=normalizeBundleSource(source);if(this.telemetry){const span=this.telemetry.startSpan("skill.bundle.swap",{source:sourceLabel,bundle_id:next.bundleId,version:next.version,skill_count:next.skills.length,...this.active?.version?{from_version:this.active.version}:{}});try{if(this.pinnedVersion!==void 0){throw new BundlePinnedError(this.pinnedVersion)}const diff=this.commit(next,"normal");span.setAttributes({status:"ok",is_no_op:diff.isNoOp});span.end();this.pullsCounter?.inc(1,{status:"ok",source:sourceLabel});return diff}catch(err){const reason=classifySwapError(err);if(span.endWithError){span.endWithError(err instanceof Error?err:String(err))}else{span.recordError?.(err instanceof Error?err:new Error(String(err)));span.end()}this.pullsCounter?.inc(1,{status:"error",source:sourceLabel,reason});throw err}}if(this.pinnedVersion!==void 0){throw new BundlePinnedError(this.pinnedVersion)}return this.commit(next,"normal")}pin(version){if(this.active&&this.active.version===version){this.pinnedVersion=version;return void 0}const entry=this.history.find(h=>h.bundle.version===version);if(!entry){throw new Error(`BundleStore.pin: version "${version}" not in history`)}const diff=this.commit(entry.bundle,"pin");this.pinnedVersion=version;return diff}rollback(){const prior=this.history[this.history.length-1];if(!prior){throw new Error("BundleStore.rollback: no prior bundle in history")}this.pinnedVersion=void 0;return this.commit(prior.bundle,"rollback")}unpin(){this.pinnedVersion=void 0}isPinned(){return this.pinnedVersion!==void 0}pinned(){return this.pinnedVersion}historySnapshot(){return this.history.map(h=>({version:h.bundle.version,bundleId:h.bundle.bundleId,validatedAt:h.validatedAt}))}subscribe(fn){this.listeners.add(fn);return()=>{this.listeners.delete(fn)}}reset(){this.active=void 0;this.history=[];this.pinnedVersion=void 0}commit(next,reason){const previous=this.active;const diff=diffBundles(previous,next);if(previous){const lastInHistory=this.history[this.history.length-1];if(!lastInHistory||lastInHistory.bundle.version!==previous.version){this.history.push({bundle:previous,validatedAt:Date.now()});if(this.history.length>this.historySize){this.history.splice(0,this.history.length-this.historySize)}}}this.active=next;for(const fn of this.listeners){try{fn({previous,current:next,diff,reason})}catch{}}return diff}};var BundlePinnedError=class extends Error{constructor(pinnedVersion){super(`BundleStore is pinned to version "${pinnedVersion}"; new swaps are rejected until unpin().`);this.pinnedVersion=pinnedVersion;this.name="BundlePinnedError"}pinnedVersion};var SkillDependencyCycleError=class extends Error{constructor(cycle){super(`skill dependency cycle: ${cycle.join(" -> ")}`);this.cycle=cycle;this.name="SkillDependencyCycleError"}cycle};var SkillDependencyMissingError=class extends Error{constructor(skillId,missingId){super(`skill "${skillId}" requires unknown skill "${missingId}"`);this.skillId=skillId;this.missingId=missingId;this.name="SkillDependencyMissingError"}skillId;missingId};var SkillDependencyInvariantError=class extends Error{constructor(message){super(message);this.name="SkillDependencyInvariantError"}};function resolveSkillLoadOrder(skills){const byId=new Map;for(const skill of skills){if(byId.has(skill.id)){byId.set(skill.id,skill);continue}byId.set(skill.id,skill)}const inDegree=new Map;const requiredBy=new Map;for(const id of byId.keys()){inDegree.set(id,0);requiredBy.set(id,[])}for(const skill of byId.values()){const deps=skill.requires??[];for(const depId of deps){if(!byId.has(depId)){throw new SkillDependencyMissingError(skill.id,depId)}inDegree.set(skill.id,(inDegree.get(skill.id)??0)+1);const reverse=requiredBy.get(depId)??[];reverse.push(skill.id);requiredBy.set(depId,reverse)}}const ready=[...byId.keys()].filter(id=>inDegree.get(id)===0).sort();const order=[];while(ready.length>0){const next=ready.shift();if(next===void 0){throw new SkillDependencyInvariantError("resolveSkillLoadOrder: invariant broken \u2014 ready queue drained mid-iteration")}const nextSkill=byId.get(next);if(!nextSkill){throw new SkillDependencyInvariantError(`resolveSkillLoadOrder: invariant broken \u2014 id "${next}" missing from byId map`)}order.push(nextSkill);for(const dependent of requiredBy.get(next)??[]){const remaining=(inDegree.get(dependent)??0)-1;inDegree.set(dependent,remaining);if(remaining===0){let i=0;while(i<ready.length&&ready[i]<dependent)i++;ready.splice(i,0,dependent)}}}if(order.length===byId.size){return order}const remainingIds=[...byId.keys()].filter(id=>(inDegree.get(id)??0)>0);const cycle=findCycle(remainingIds,byId);throw new SkillDependencyCycleError(cycle)}function findCycle(remaining,byId){const adj=new Map;for(const id of remaining){adj.set(id,(byId.get(id)?.requires??[]).filter(d=>remaining.includes(d)))}const WHITE=0;const GRAY=1;const BLACK=2;const color=new Map;for(const id of remaining)color.set(id,WHITE);const stack=[];function dfs(node){color.set(node,GRAY);stack.push(node);for(const next of adj.get(node)??[]){const c=color.get(next);if(c===GRAY){const startIdx=stack.indexOf(next);return[...stack.slice(startIdx),next]}if(c===WHITE){const found=dfs(next);if(found)return found}}color.set(node,BLACK);stack.pop();return void 0}for(const id of remaining){if(color.get(id)===WHITE){const cycle=dfs(id);if(cycle)return cycle}}return remaining}var import_lazy_zod2=require("@frontmcp/lazy-zod");var httpsUrl=import_lazy_zod2.z.string().url().refine(v=>{try{return new URL(v).protocol==="https:"}catch{return false}},{message:"must use https://"});var staticSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("static"),path:import_lazy_zod2.z.string().min(1),watch:import_lazy_zod2.z.boolean().default(false)});var npmSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("npm"),packageName:import_lazy_zod2.z.string().min(1),exportName:import_lazy_zod2.z.string().optional(),verifyProvenance:import_lazy_zod2.z.boolean().default(true)});var saasSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("saas"),endpoint:httpsUrl,authToken:import_lazy_zod2.z.string().min(1),expectedAudience:import_lazy_zod2.z.string().min(1),pollIntervalMs:import_lazy_zod2.z.number().int().positive().default(3e5),enableWebhook:import_lazy_zod2.z.boolean().default(false),jwksUrl:httpsUrl,expectedIssuer:import_lazy_zod2.z.string().min(1)});var inlineSourceSchema=import_lazy_zod2.z.object({type:import_lazy_zod2.z.literal("inline"),content:import_lazy_zod2.z.unknown()});var bundleSourceSchema=import_lazy_zod2.z.discriminatedUnion("type",[staticSourceSchema,npmSourceSchema,saasSourceSchema,inlineSourceSchema]);var signatureKeySchema=import_lazy_zod2.z.object({keyId:import_lazy_zod2.z.string().min(1),alg:import_lazy_zod2.z.enum(["RS256","EdDSA"]),publicKeyPem:import_lazy_zod2.z.string().min(1)});var InlineSource=class{constructor(options,logger){this.options=options;this.logger=logger}options;logger;id="inline:bundle";listeners=new Set;async start(){const bundle=this.parse();for(const fn of this.listeners){try{fn(bundle)}catch(e){this.logger.warn(`[inline-source] listener threw: ${e.message}`)}}}onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async stop(){this.listeners.clear()}parse(){return parseOverlay({kind:"object",content:this.options.content})}};var npmDynamicImport;var NpmSource=class{constructor(options,logger){this.options=options;this.logger=logger;this.id=`npm:${this.options.packageName}${this.options.exportName?`#${this.options.exportName}`:""}`}options;logger;id;listeners=new Set;cached;async start(){const bundle=await this.loadBundleFromPackage();this.cached=bundle;this.notify(bundle)}onChange(listener){this.listeners.add(listener);if(this.cached){try{listener(this.cached)}catch(e){this.logger.warn(`[npm-source] listener threw on replay: ${e.message}`)}}return()=>{this.listeners.delete(listener)}}async stop(){this.listeners.clear()}async loadBundleFromPackage(){if(this.options.verifyProvenance){throw new Error(`npm source "${this.options.packageName}": verifyProvenance is true, but npm provenance verification is not implemented, so the package was not loaded. Set verifyProvenance: false to load it without a provenance check, only for a package you trust: importing it runs its code before any bundle signature is checked, so pin its exact version and install it from a lockfile with integrity hashes.`)}const mod=await this.dynamicImport(this.options.packageName);const exportName=this.options.exportName??"default";const modRecord=mod;const exported=modRecord[exportName]??(exportName==="default"?mod:void 0);if(exported===void 0||exported===null){throw new Error(`npm source "${this.options.packageName}": export "${exportName}" not found on the package`)}return parseOverlay({kind:"object",content:exported})}dynamicImport(specifier){npmDynamicImport??=new Function("s","return import(s)");return npmDynamicImport(specifier)}notify(bundle){for(const fn of this.listeners){try{fn(bundle)}catch(e){this.logger.warn(`[npm-source] listener threw: ${e.message}`)}}}};var import_auth=require("@frontmcp/auth");var import_utils2=require("@frontmcp/utils");var import_utils=require("@frontmcp/utils");var DEFAULT_WINDOW_MS=5*60*1e3;var DEFAULT_CAPACITY=5e3;var WebhookReplayGuard=class{windowMs;capacity;seen=new Map;now=Date.now;rejectsCounter;checksCounter;constructor(options={}){const windowMs=options.windowMs??DEFAULT_WINDOW_MS;const capacity=options.capacity??DEFAULT_CAPACITY;if(!Number.isFinite(windowMs)||windowMs<=0){throw new RangeError("WebhookReplayGuard: windowMs must be a finite number > 0")}if(!Number.isInteger(capacity)||capacity<=0){throw new RangeError("WebhookReplayGuard: capacity must be a positive integer")}this.windowMs=windowMs;this.capacity=capacity;this.rejectsCounter=options.telemetry?.createCounter("frontmcp_skills_replay_rejects_total","Number of webhook replay-guard rejections, partitioned by reason.");this.checksCounter=options.telemetry?.createCounter("frontmcp_skills_replay_checks_total","Number of webhook replay-guard checks, partitioned by status (ok|error).")}setNowProvider(fn){this.now=fn}check(args){const{timestampMs,nonce}=args;const reject=(code,reason)=>{this.rejectsCounter?.inc(1,{reason:code});this.checksCounter?.inc(1,{status:"error"});return{ok:false,reason,code}};if(!Number.isFinite(timestampMs)){return reject("invalid_timestamp","timestamp not finite")}if(typeof nonce!=="string"||nonce.length<8){return reject("invalid_nonce","nonce too short")}const now=this.now();if(Math.abs(now-timestampMs)>this.windowMs){return reject("outside_window",`timestamp outside ${this.windowMs}ms window`)}const key=(0,import_utils.sha256Hex)(nonce);const expiresAt=this.seen.get(key);if(expiresAt!==void 0&&expiresAt>=now){return reject("nonce_replay","nonce replay detected within freshness window")}if(this.seen.size>=this.capacity){this.prune()}if(this.seen.size>=this.capacity){let victimKey;let victimExpiry=Number.POSITIVE_INFINITY;for(const[candidateKey,candidateExpiry]of this.seen){if(candidateExpiry<victimExpiry){victimKey=candidateKey;victimExpiry=candidateExpiry}}if(victimKey!==void 0)this.seen.delete(victimKey)}this.seen.set(key,timestampMs+this.windowMs);this.checksCounter?.inc(1,{status:"ok"});return{ok:true}}prune(){const now=this.now();let removed=0;for(const[k,expiresAt]of this.seen){if(expiresAt<now){this.seen.delete(k);removed++}}return removed}get size(){return this.seen.size}};var DEFAULT_CACHE_DIR=".frontmcp/skilled-openapi";var DEFAULT_PULL_TIMEOUT_MS=3e4;var USER_AGENT="frontmcp-skilled-openapi";var PUSH_SIGNING_KEYS_TTL_MS=6e4;var MAX_LISTED_ISSUES=3;var SaasPullTokenRejectedError=class extends Error{constructor(reason){super(`[saas-source] pull token rejected: ${reason}`);this.name="SaasPullTokenRejectedError"}};var SaasPushRefusedError=class extends Error{constructor(refusal,reason){super(`[saas-source] push refused: ${reason}`);this.refusal=refusal;this.name="SaasPushRefusedError"}refusal};function describePullFailure(error){const message=error instanceof Error?error.message:String(error);if(!(error instanceof OverlayParseError)||!error.errors?.length)return message;return`${message}: ${error.errors.slice(0,MAX_LISTED_ISSUES).join("; ")}`}function claimValues(claim){if(Array.isArray(claim))return claim;return claim===void 0?[]:[claim]}var PUBLIC_KEY_MEMBERS=new Map([["RSA",["n","e"]],["EC",["crv","x","y"]],["OKP",["crv","x"]]]);function isUsableSigningKey(key){if(typeof key!=="object"||key===null)return false;const jwk=key;const members=typeof jwk["kty"]==="string"?PUBLIC_KEY_MEMBERS.get(jwk["kty"]):void 0;if(!members||jwk["use"]!==void 0&&jwk["use"]!=="sig")return false;return members.every(m=>typeof jwk[m]==="string"&&jwk[m]!=="")}var SaasPullSource=class{constructor(options,cacheDir,logger,deps={}){this.options=options;this.cacheDir=cacheDir;this.logger=logger;this.deps=deps;this.id=`saas:${this.options.endpoint}`;if(options.enableWebhook)this.replayGuard=new WebhookReplayGuard}options;cacheDir;logger;deps;id;listeners=new Set;pollHandle;inFlightPull;stopped=false;jwksService=new import_auth.JwksService({});replayGuard;pushSigningKeys;pushSigningKeysLoading;get push(){return this.replayGuard?this:void 0}async verifyPushToken(token){if(!this.replayGuard){throw new SaasPushRefusedError("invalid-token","this source does not take pushed bundles (enableWebhook is off)")}const verdict=await this.verifyIssuedToken(token,await this.signingKeysForPush());if(!verdict.ok)throw new SaasPushRefusedError("invalid-token",verdict.reason);const{iat,jti}=verdict.claims;if(typeof iat!=="number"||typeof jti!=="string"){throw new SaasPushRefusedError("invalid-token",'a push token needs "iat" and "jti" claims')}const replay=this.replayGuard.check({timestampMs:iat*1e3,nonce:jti});if(!replay.ok){const refusal=replay.code==="nonce_replay"?"replayed":"invalid-token";throw new SaasPushRefusedError(refusal,`its "jti" or "iat" claim: ${replay.reason}`)}}keepBundle(bundle){return this.persistCache(bundle)}async start(){if(this.stopped||this.inFlightPull){this.schedulePoll();return}try{await this.exclusivePull(()=>this.pullAtStart())}finally{this.schedulePoll()}}async pullAtStart(){let bundle;try{bundle=await this.fetchOnce()}catch(e){if(e instanceof SaasPullTokenRejectedError)throw e;const failure=describePullFailure(e);this.logger.warn(`[saas-source] initial pull failed (${failure}); attempting cached bundle fallback`);bundle=await this.loadCache();if(!bundle){throw new Error(`[saas-source] initial pull failed and no cached bundle is available ${this.cacheLocation()}: ${failure}`,{cause:e})}this.logger.warn(`[saas-source] using cached bundle "${bundle.bundleId}@${bundle.version}"`)}await this.notify(bundle)}exclusivePull(pull){const running=pull().finally(()=>{this.inFlightPull=void 0});this.inFlightPull=running;return running}onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async stop(){this.stopped=true;if(this.pollHandle){clearTimeout(this.pollHandle);this.pollHandle=void 0}this.listeners.clear()}schedulePoll(){if(this.stopped||this.deps.disablePolling)return;if(this.pollHandle)clearTimeout(this.pollHandle);this.pollHandle=setTimeout(()=>{void this.pollOnce()},this.options.pollIntervalMs);this.pollHandle.unref?.()}async refresh(){if(this.stopped)return void 0;if(this.inFlightPull){await this.inFlightPull.catch(()=>void 0);return void 0}return this.exclusivePull(async()=>{const bundle=await this.fetchOnce();await this.notify(bundle);return bundle})}async pollOnce(){if(this.stopped)return;if(this.inFlightPull){this.schedulePoll();return}try{await this.exclusivePull(async()=>{const bundle=await this.fetchOnce();await this.notify(bundle)})}catch(e){if(e instanceof SaasPullTokenRejectedError){this.logger.error(`[saas-source] poll refused: ${e.message}; keeping the current bundle`)}else{this.logger.warn(`[saas-source] poll failed: ${describePullFailure(e)}`)}}finally{this.schedulePoll()}}async httpGet(url,headers){const controller=new AbortController;const timer=setTimeout(()=>controller.abort(),DEFAULT_PULL_TIMEOUT_MS);try{const res=await fetch(url,{method:"GET",headers,signal:controller.signal,redirect:"manual"});if((0,import_utils2.isRedirectResponse)(res)){throw new Error(`pull endpoint returned a redirect (${res.status}); not followed to protect the bearer token`)}const body=await res.text();return{status:res.status,body}}finally{clearTimeout(timer)}}async verifyPullToken(){const verdict=await this.verifyIssuedToken(this.options.authToken,await this.fetchSigningKeys());if(!verdict.ok)throw new SaasPullTokenRejectedError(verdict.reason)}signingKeysForPush(){const cached=this.pushSigningKeys;if(cached&&Date.now()-cached.fetchedAt<PUSH_SIGNING_KEYS_TTL_MS)return Promise.resolve(cached.keys);this.pushSigningKeysLoading??=this.fetchSigningKeys().then(keys=>{this.pushSigningKeys={keys,fetchedAt:Date.now()};return keys}).finally(()=>{this.pushSigningKeysLoading=void 0});return this.pushSigningKeysLoading}async fetchSigningKeys(){const{jwksUrl}=this.options;const{status,body}=await this.httpGet(jwksUrl,{Accept:"application/json","User-Agent":USER_AGENT});if(status<200||status>=300){throw new Error(`JWKS ${jwksUrl} responded ${status}`)}let jwks;try{jwks=JSON.parse(body)}catch{throw new Error(`JWKS ${jwksUrl} is not valid JSON`)}if(!jwks||!Array.isArray(jwks.keys)||jwks.keys.length===0){throw new Error(`JWKS ${jwksUrl} has no keys`)}const signingKeys=jwks.keys.filter(isUsableSigningKey);if(signingKeys.length===0){throw new Error(`JWKS ${jwksUrl} has no usable signing keys`)}return signingKeys}async verifyIssuedToken(token,signingKeys){const{jwksUrl,expectedIssuer,expectedAudience}=this.options;const verified=await this.jwksService.verifyTransparentToken(token,[{id:"skilled-openapi-saas",issuerUrl:expectedIssuer,jwks:{keys:signingKeys}}]);if(!verified.ok||!verified.payload){return{ok:false,reason:`not a valid token from issuer "${expectedIssuer}" signed by a key in ${jwksUrl} (${verified.error??"verification failed"})`}}const payload=verified.payload;if(!claimValues(payload["aud"]).includes(expectedAudience)){return{ok:false,reason:`its "aud" claim does not include "${expectedAudience}"`}}const resource=claimValues(payload["resource"]);if(resource.length>0&&!resource.includes(expectedAudience)){return{ok:false,reason:`its "resource" claim does not include "${expectedAudience}"`}}return{ok:true,claims:payload}}async fetchOnce(){await this.verifyPullToken();const{status,body}=await this.httpGet(this.options.endpoint,{Authorization:`Bearer ${this.options.authToken}`,Accept:"application/json, application/yaml","User-Agent":USER_AGENT});if(status<200||status>=300){throw new Error(`pull responded ${status}`)}const trimmed=body.trimStart();return parseOverlay(trimmed.startsWith("{")||trimmed.startsWith("[")?{kind:"json",content:body}:{kind:"yaml",content:body})}cacheLocation(){return this.deps.cache?"in the injected bundle cache":`at ${this.cachePath()}`}cachePath(){const dir=this.cacheDir??DEFAULT_CACHE_DIR;return(0,import_utils2.pathResolve)(dir,`${this.options.expectedAudience.replace(/[^a-zA-Z0-9_.-]/g,"_")}.json`)}async persistCache(bundle){try{if(this.deps.cache){await this.deps.cache.write(bundle);return}const filePath=this.cachePath();await(0,import_utils2.ensureDir)((0,import_utils2.dirname)(filePath));await(0,import_utils2.writeFile)(filePath,JSON.stringify(bundle))}catch(e){this.logger.warn(`[saas-source] cache persist failed: ${e.message}`)}}async loadCache(){try{if(this.deps.cache){const cached=await this.deps.cache.read();if(cached===void 0||cached===null)return void 0;return parseOverlay({kind:"object",content:cached})}const filePath=this.cachePath();const raw=await(0,import_utils2.readFile)(filePath,"utf8");return parseOverlay({kind:"json",content:raw})}catch{return void 0}}async notify(bundle){for(const fn of this.listeners){try{await fn(bundle)}catch(e){this.logger.warn(`[saas-source] listener threw: ${e.message}`)}}}};var import_utils3=require("@frontmcp/utils");var StaticSource=class{constructor(options,logger){this.options=options;this.logger=logger;this.id=`static:${this.options.path}`}options;logger;id;listeners=new Set;watcher;debounceTimer;stopped=false;lastContent;async start(){if(this.options.watch){this.beginWatch()}const content=await this.readBundleFile();this.lastContent=content;this.notify(this.parse(content))}onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async stop(){this.stopped=true;if(this.watcher){this.watcher.close();this.watcher=void 0}if(this.debounceTimer){clearTimeout(this.debounceTimer);this.debounceTimer=void 0}this.listeners.clear()}readBundleFile(){return(0,import_utils3.readFile)((0,import_utils3.pathResolve)(this.options.path),"utf8")}parse(raw){const ext=(0,import_utils3.extname)(this.options.path).toLowerCase();let input;if(ext===".json"){input={kind:"json",content:raw}}else if(ext===".yaml"||ext===".yml"){input={kind:"yaml",content:raw}}else{const trimmed=raw.trimStart();input=trimmed.startsWith("{")||trimmed.startsWith("[")?{kind:"json",content:raw}:{kind:"yaml",content:raw}}return parseOverlay(input)}notify(bundle){for(const fn of this.listeners){try{fn(bundle)}catch(e){this.logger.warn(`[static-source] listener threw: ${e.message}`)}}}beginWatch(){const absPath=(0,import_utils3.pathResolve)(this.options.path);try{this.watcher=(0,import_utils3.watchFile)(absPath,()=>this.scheduleRefresh())}catch(e){this.logger.warn(`[static-source] fs.watch failed (${e.message}); polling at 2s instead`);const tick=()=>{if(this.stopped)return;this.scheduleRefresh();setTimeout(tick,2e3).unref?.()};setTimeout(tick,2e3).unref?.()}}scheduleRefresh(){if(this.debounceTimer)clearTimeout(this.debounceTimer);this.debounceTimer=setTimeout(async()=>{if(this.stopped)return;try{const content=await this.readBundleFile();if(content===this.lastContent)return;this.lastContent=content;this.notify(this.parse(content))}catch(e){this.logger.warn(`[static-source] refresh failed: ${e.message}`)}},250);this.debounceTimer.unref?.()}};function createBundleSource(source,cacheDir,logger,deps){switch(source.type){case"static":return new StaticSource(source,logger);case"npm":return new NpmSource(source,logger);case"saas":return new SaasPullSource(source,cacheDir,logger,deps);case"inline":return new InlineSource(source,logger)}}var import_mcp_from_openapi=require("mcp-from-openapi");var BODYLESS_STATUS_CODES=new Set([204,205]);var DEFAULT_RESPONSE_STATUS=0;var SUCCESS_RANGE_STATUS=2;function isSuccessWithBody(statusCode){if(statusCode===DEFAULT_RESPONSE_STATUS||statusCode===SUCCESS_RANGE_STATUS)return true;return typeof statusCode==="number"&&statusCode>=200&&statusCode<300&&!BODYLESS_STATUS_CODES.has(statusCode)}function acceptAnyUndeclaredBody(schema){const undeclared=schema.type==="null"&&schema["x-content-type"]===void 0&&isSuccessWithBody(schema["x-status-code"]);if(!undeclared)return schema;const{type:_null,...annotations}=schema;return annotations}function outputSchemaOf(tool){const outputSchema=tool.outputSchema;if(!outputSchema||tool.metadata.method==="head")return outputSchema;const statusBranches=outputSchema.oneOf;if(statusBranches&&outputSchema["x-status-code"]===void 0){const{oneOf:_exclusive,...union}=outputSchema;return{...union,anyOf:statusBranches.map(acceptAnyUndeclaredBody)}}return acceptAnyUndeclaredBody(outputSchema)}async function compileSkilledBundleFromOpenApi(openapi,skills,options){const serviceId=options.serviceId??"api";const baseUrl=options.baseUrl??openapi.servers?.[0]?.url;if(!baseUrl){throw new Error("compileSkilledBundleFromOpenApi: no baseUrl \u2014 pass options.baseUrl or declare servers[] in the OpenAPI doc.")}const generator=await import_mcp_from_openapi.OpenAPIToolGenerator.fromJSON(openapi);const tools=await generator.generateTools();const byOperationId=new Map;for(const tool of tools){const opId=tool.metadata.operationId;if(opId)byOperationId.set(opId,tool)}const referenced=new Set;for(const skill of skills)for(const opId of skill.operationIds)referenced.add(opId);const operations={};for(const opId of referenced){const tool=byOperationId.get(opId);if(!tool){throw new Error(`compileSkilledBundleFromOpenApi: operationId "${opId}" is referenced by a skill but not defined in the OpenAPI document.`)}const tags=operationTags(tool);operations[opId]={operationId:opId,serviceId,httpMethod:tool.metadata.method.toUpperCase(),pathTemplate:tool.metadata.path,summary:tool.metadata.operationSummary??tool.description,...tags.length>0?{tags}:{},inputSchema:tool.inputSchema,outputSchema:outputSchemaOf(tool)??{},mapper:tool.mapper,authBindingRef:"default"}}return{schemaVersion:1,bundleId:options.bundleId,version:options.version,generatedAt:options.generatedAt??new Date().toISOString(),sourceDigest:options.sourceDigest??"0".repeat(64),services:[{id:serviceId,baseUrl}],authBindings:{default:options.authBinding??{kind:"none"}},skills:skills.map(s=>({id:s.id,name:s.name,description:s.description,instructions:s.instructions,tags:s.tags??[],operationIds:s.operationIds})),operations}}function operationTags(tool){return[...new Set([...tool.metadata.tags??[],...tool.metadata.frontmcp?.tags??[]])]}var import_utils4=require("@frontmcp/utils");var DEFAULT_OPTIONS={skillsDir:"./skills",watch:true,debounceMs:200,pollIntervalMs:2e3};var FilesystemSkillsSource=class{constructor(options,logger){this.logger=logger;this.options={...DEFAULT_OPTIONS,...options};this.id=`filesystem-skills:${this.options.skillsDir}`}logger;id;options;listeners=new Set;known=new Map;watcher;pollTimer;debounceTimers=new Map;stopped=false;rootResolved="";onChange(listener){this.listeners.add(listener);return()=>{this.listeners.delete(listener)}}async start(){const absRoot=(0,import_utils4.pathResolve)(this.options.skillsDir);try{this.rootResolved=await(0,import_utils4.realpath)(absRoot)}catch{this.rootResolved=absRoot}await this.initialScan();if(this.options.watch){this.beginWatch()}}async stop(){this.stopped=true;if(this.watcher){try{this.watcher.close()}catch{}this.watcher=void 0}if(this.pollTimer){clearTimeout(this.pollTimer);this.pollTimer=void 0}for(const t of this.debounceTimers.values())clearTimeout(t);this.debounceTimers.clear();this.listeners.clear();this.known.clear()}async initialScan(){const entries=await this.listSkillSubdirs();for(const dir of entries){await this.refreshSkill(dir)}}async listSkillSubdirs(){let entries;try{entries=await(0,import_utils4.readdir)(this.rootResolved)}catch(e){this.logger.warn(`[fs-skills] cannot read ${this.rootResolved}: ${e.message}`);return[]}const out=[];for(const name of entries){if(name.startsWith("."))continue;const abs=(0,import_utils4.joinPath)(this.rootResolved,name);let resolved;try{resolved=await(0,import_utils4.realpath)(abs)}catch{continue}if(!resolved.startsWith(this.rootResolved+import_utils4.sep)&&resolved!==this.rootResolved){this.logger.warn(`[fs-skills] skipping ${name}: realpath escapes skillsDir`);continue}try{const st=await(0,import_utils4.stat)(resolved);if(st.isDirectory())out.push(resolved)}catch{}}return out.sort()}async refreshSkill(absDir){if(this.stopped)return;let exists=false;try{const st=await(0,import_utils4.stat)(absDir);exists=st.isDirectory()}catch{exists=false}if(!exists){const prev2=this.known.get(absDir);if(prev2){this.known.delete(absDir);this.emit({op:"delete",skillId:prev2.id,absPath:absDir})}return}let content;try{content=await loadFilesystemSkill(absDir)}catch(e){this.logger.warn(`[fs-skills] failed to load ${absDir}: ${e.message}`);return}const prev=this.known.get(absDir);if(prev&&skillContentEqual(prev,content)){return}this.known.set(absDir,content);this.emit({op:"upsert",skill:content,absPath:absDir})}emit(event){for(const fn of this.listeners){try{fn(event)}catch(e){this.logger.warn(`[fs-skills] listener threw: ${e.message}`)}}}beginWatch(){try{this.watcher=(0,import_utils4.watchFile)(this.rootResolved,(_eventType,filename)=>{if(this.stopped)return;if(!filename){this.scheduleRescan();return}const first=filename.split(import_utils4.sep)[0];if(!first||first.startsWith("."))return;const absDir=(0,import_utils4.joinPath)(this.rootResolved,first);this.scheduleRefresh(absDir)},{recursive:true});this.watcher.onError(e=>{this.logger.warn(`[fs-skills] fs.watch error: ${e.message}; falling back to polling`);this.watcher?.close();this.watcher=void 0;this.beginPolling()})}catch(e){this.logger.warn(`[fs-skills] fs.watch failed (${e.message}); polling at ${this.options.pollIntervalMs}ms instead`);this.beginPolling()}}beginPolling(){const tick=async()=>{if(this.stopped)return;try{await this.pollOnce()}catch(e){this.logger.warn(`[fs-skills] poll tick error: ${e.message}`)}if(this.stopped)return;this.pollTimer=setTimeout(tick,this.options.pollIntervalMs);this.pollTimer.unref?.()};this.pollTimer=setTimeout(tick,this.options.pollIntervalMs);this.pollTimer.unref?.()}async pollOnce(){const dirs=new Set(await this.listSkillSubdirs());for(const known of[...this.known.keys()]){if(!dirs.has(known)){await this.refreshSkill(known)}}for(const abs of dirs){await this.refreshSkill(abs)}}scheduleRescan(){const key="<rescan>";const existing=this.debounceTimers.get(key);if(existing)clearTimeout(existing);const t=setTimeout(()=>{this.debounceTimers.delete(key);void this.pollOnce().catch(e=>{this.logger.warn(`[fs-skills] rescan failed: ${e.message}`)})},this.options.debounceMs);t.unref?.();this.debounceTimers.set(key,t)}scheduleRefresh(absDir){const existing=this.debounceTimers.get(absDir);if(existing)clearTimeout(existing);const t=setTimeout(()=>{this.debounceTimers.delete(absDir);void this.refreshSkill(absDir).catch(e=>{this.logger.warn(`[fs-skills] refresh failed for ${absDir}: ${e.message}`)})},this.options.debounceMs);t.unref?.();this.debounceTimers.set(absDir,t)}};async function loadFilesystemSkill(absDir){const skillMdPath=(0,import_utils4.joinPath)(absDir,"SKILL.md");const raw=await(0,import_utils4.readFile)(skillMdPath,"utf8");const{frontmatter,body}=parseSkillFrontmatter(raw);const dirName=(0,import_utils4.basename)(absDir);const name=frontmatter["name"]??dirName;const description=frontmatter["description"]??"";if(!description){throw new Error(`SKILL.md missing 'description' frontmatter (in ${absDir})`)}const instructions=body.trim();if(!instructions){throw new Error(`SKILL.md missing body content (in ${absDir})`)}const tools=parseToolList(frontmatter["tools"]);const resources={};for(const sub of["scripts","references","examples","assets"]){const subAbs=(0,import_utils4.joinPath)(absDir,sub);try{const st=await(0,import_utils4.stat)(subAbs);if(st.isDirectory())resources[sub]=subAbs}catch{}}const out={id:name,name,description,instructions,tools,...Object.keys(resources).length>0&&{resources}};if(typeof frontmatter["license"]==="string")out.license=frontmatter["license"];if(typeof frontmatter["compatibility"]==="string")out.compatibility=frontmatter["compatibility"];return out}function parseSkillFrontmatter(raw){if(!raw.startsWith("---")){return{frontmatter:{},body:raw}}const closeIdx=raw.indexOf("\n---",3);if(closeIdx===-1){return{frontmatter:{},body:raw}}const yamlBlock=raw.slice(3,closeIdx).trim();let bodyStart=closeIdx+4;if(raw[bodyStart]==="\n")bodyStart+=1;const body=raw.slice(bodyStart);const fm={};let currentListKey;for(const line of yamlBlock.split("\n")){if(!line.trim())continue;if(line.startsWith("- ")&¤tListKey){const arr=fm[currentListKey]??[];arr.push(stripQuotes(line.slice(2).trim()));fm[currentListKey]=arr;continue}const colon=line.indexOf(":");if(colon===-1)continue;const key=line.slice(0,colon).trim();const rest=line.slice(colon+1).trim();if(!rest){currentListKey=key;fm[key]=[];continue}currentListKey=void 0;fm[key]=stripQuotes(rest)}return{frontmatter:fm,body}}function stripQuotes(s){if(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'")){return s.slice(1,-1)}return s}function parseToolList(raw){if(Array.isArray(raw)){return raw.map(t=>typeof t==="string"?{name:t}:null).filter(t=>t!==null)}if(typeof raw==="string"){return raw.split(",").map(t=>t.trim()).filter(Boolean).map(name=>({name}))}return[]}function skillContentEqual(a,b){return a.id===b.id&&a.name===b.name&&a.description===b.description&&a.instructions===b.instructions&&a.license===b.license&&a.compatibility===b.compatibility&&JSON.stringify(a.tools)===JSON.stringify(b.tools)&&JSON.stringify(a.resources??{})===JSON.stringify(b.resources??{})}var import_utils5=require("@frontmcp/utils");function classifyReason(reason){if(reason.includes("missing integrity"))return"missing_integrity";if(reason.includes("digest mismatch"))return"digest_mismatch";if(reason.includes("unknown signing keyId"))return"unknown_key_id";if(reason.includes("alg="))return"alg_mismatch";if(reason.includes("parse public key"))return"malformed_public_key";if(reason.includes("not valid base64url"))return"malformed_signature";if(reason.includes("signature verify threw"))return"verify_threw";if(reason.includes("signature verification failed"))return"verify_failed";return"other"}function canonicalize(value){if(value===null||typeof value!=="object"){return JSON.stringify(value)}if(Array.isArray(value)){return`[${value.map(v=>canonicalize(v)).join(",")}]`}const obj=value;const keys=Object.keys(obj).sort();return`{${keys.map(k=>`${JSON.stringify(k)}:${canonicalize(obj[k])}`).join(",")}}`}function bundleDigest(bundle){const{integrity:_drop,...rest}=bundle;void _drop;return(0,import_utils5.sha256Hex)(canonicalize(rest))}function decodeSignatureBytes(signatureBase64Url){try{return(0,import_utils5.base64urlDecode)(signatureBase64Url)}catch{return void 0}}function verifyBundleSignature(bundle,trustedKeys,telemetry){const failuresCounter=telemetry?.createCounter("frontmcp_skills_signature_failures_total","Number of bundle signature verification failures, partitioned by reason.");const verificationsCounter=telemetry?.createCounter("frontmcp_skills_signature_verifications_total","Number of bundle signature verification attempts, partitioned by status (ok|error).");const fail=reason=>{failuresCounter?.inc(1,{reason:classifyReason(reason)});verificationsCounter?.inc(1,{status:"error"});return{ok:false,reason}};if(!bundle.integrity){return fail("bundle missing integrity envelope")}const{alg,keyId,signature,digest}=bundle.integrity;const expectedDigest=bundleDigest(bundle);if(digest.toLowerCase()!==expectedDigest.toLowerCase()){return fail(`digest mismatch (envelope=${digest.slice(0,12)}.. computed=${expectedDigest.slice(0,12)}..)`)}const trusted=trustedKeys.find(k=>k.keyId===keyId);if(!trusted){return fail(`unknown signing keyId "${keyId}" \u2014 not in trustedKeys allowlist`)}if(trusted.alg!==alg){return fail(`key "${keyId}" alg=${trusted.alg} but envelope alg=${alg}`)}let publicJwk;try{publicJwk=(0,import_utils5.pemToPublicJwk)(trusted.publicKeyPem)}catch(e){return fail(`failed to parse public key for "${keyId}": ${e.message}`)}const sigBytes=decodeSignatureBytes(signature);if(!sigBytes){return fail("signature is not valid base64url")}const{integrity:_drop,...rest}=bundle;void _drop;const canonicalBytes=new TextEncoder().encode(canonicalize(rest));const verified=(0,import_utils5.rsaVerifySync)(alg,canonicalBytes,publicJwk,sigBytes);if(!verified){return fail("signature verification failed")}verificationsCounter?.inc(1,{status:"ok"});return{ok:true,keyId,alg}}var import_auth2=require("@frontmcp/auth");var DEFAULT_REQUIRED_ROLES=["frontmcp:cloud:push"];var BundlePushJwtVerifier=class{constructor(options,logger,jwks){this.options=options;this.logger=logger;this.jwks=jwks??new import_auth2.JwksService({});this.providerRef={id:"skilled-openapi-cloud-push",issuerUrl:options.expectedIssuer,jwksUri:options.jwksUri}}options;logger;providerRef;jwks;async verify(token){if(!token||typeof token!=="string"){return{ok:false,reason:"token missing"}}let res;try{res=await this.jwks.verifyTransparentToken(token,[this.providerRef])}catch(e){this.logger.debug(`[skilled-openapi] jwt verify threw: ${e.message}`);return{ok:false,reason:`verify failed: ${e.message}`}}if(!res.ok||!res.payload){return{ok:false,reason:res.error??"signature verify failed"}}const payload=res.payload;const aud=payload["aud"];const audValues=Array.isArray(aud)?aud:aud!==void 0?[aud]:[];if(!audValues.includes(this.options.expectedAudience)){return{ok:false,reason:`aud mismatch (expected "${this.options.expectedAudience}")`}}const resource=payload["resource"];const resValues=Array.isArray(resource)?resource:resource!==void 0?[resource]:[];if(!resValues.includes(this.options.expectedResource)){return{ok:false,reason:`RFC 8707 resource indicator mismatch (expected "${this.options.expectedResource}")`}}const requiredRoles=this.options.requiredRoles??DEFAULT_REQUIRED_ROLES;if(requiredRoles.length>0&&!this.matchesAny(payload["roles"],requiredRoles)){return{ok:false,reason:`missing required role (need one of ${requiredRoles.join(", ")})`}}if(this.options.requiredScopes&&this.options.requiredScopes.length>0){if(!this.matchesAny(payload["scope"]??payload["scp"],this.options.requiredScopes)){return{ok:false,reason:`missing required scope (need one of ${this.options.requiredScopes.join(", ")})`}}}return{ok:true,payload}}matchesAny(claim,required){const have=Array.isArray(claim)?claim.filter(v=>typeof v==="string"):typeof claim==="string"?claim.split(/\s+/).filter(Boolean):[];return required.some(r=>have.includes(r))}};var SKILL_AUDIT_GENESIS_PREV_HASH="0".repeat(64);var SKILL_AUDIT_KEYS={sequence:"audit:skills:sequence",record:sequence=>`audit:skills:records:${sequence}`};var import_utils6=require("@frontmcp/utils");function canonicalizeRecordForSigning(record){const{signature:_drop,...rest}=record;void _drop;return canonicalize(rest)}function nextPrevHash(prev){if(!prev)return SKILL_AUDIT_GENESIS_PREV_HASH;return(0,import_utils6.sha256Hex)(canonicalizeRecordForSigning(prev))}function linkRecord(prev,partial){return{...partial,prevHash:nextPrevHash(prev)}}function verifyChain(records,trustedKeys,verifier,options={}){if(records.length===0){return{ok:true,verified:0}}let prev=options.previous;for(let i=0;i<records.length;i++){const rec=records[i];if(!rec){return{ok:false,breakAt:i,reason:"record at index is null/undefined"}}if(prev&&rec.sequence!==prev.sequence+1){return{ok:false,breakAt:rec.sequence,reason:`sequence gap: expected ${prev.sequence+1}, got ${rec.sequence}`}}const expectedPrev=nextPrevHash(prev);if(rec.prevHash!==expectedPrev){return{ok:false,breakAt:rec.sequence,reason:`prevHash mismatch at sequence ${rec.sequence}: expected ${expectedPrev.slice(0,12)}.. got ${rec.prevHash.slice(0,12)}..`}}const canonicalBytes=new TextEncoder().encode(canonicalizeRecordForSigning(rec));const ok=verifier({alg:rec.signatureAlg,keyId:rec.signatureKeyId,data:canonicalBytes,signatureBase64Url:rec.signature,trustedKeys:[...trustedKeys]});if(!ok){return{ok:false,breakAt:rec.sequence,reason:`signature verification failed at sequence ${rec.sequence} (keyId="${rec.signatureKeyId}", alg=${rec.signatureAlg})`}}prev=rec}return{ok:true,verified:records.length}}var import_utils7=require("@frontmcp/utils");function deriveFromKeyMaterial(ikm,info,length){return(0,import_utils7.hkdfSha256)(ikm,new Uint8Array(0),new TextEncoder().encode(info),length)}var Hs256AuditSigner=class{key;keyId;constructor(secret,keyId){if(!keyId){throw new Error("Hs256AuditSigner: keyId must be a non-empty string")}if(typeof secret==="string"){if(secret.length===0)throw new Error("Hs256AuditSigner: secret must be non-empty");this.key=new TextEncoder().encode(secret)}else{if(secret.length===0)throw new Error("Hs256AuditSigner: secret must be non-empty");this.key=new Uint8Array(secret)}this.keyId=keyId}sign(record){const canonicalBytes=new TextEncoder().encode(canonicalizeRecordForSigning({...record,signature:"",signatureKeyId:this.keyId,signatureAlg:"HS256"}));const mac=(0,import_utils7.hmacSha256)(this.key,canonicalBytes);return{signature:(0,import_utils7.base64urlEncode)(mac),keyId:this.keyId,alg:"HS256"}}getKeyId(){return this.keyId}getAlg(){return"HS256"}deriveKey(info,length){return deriveFromKeyMaterial(this.key,info,length)}};var Rs256AuditSigner=class{privateJwk;privateExponent;keyId;constructor(privateJwk,keyId){if(!keyId){throw new Error("Rs256AuditSigner: keyId must be a non-empty string")}if(!privateJwk||typeof privateJwk!=="object"){throw new Error("Rs256AuditSigner: privateJwk must be a JWK object")}if(privateJwk.kty!=="RSA"){throw new Error(`Rs256AuditSigner: privateJwk.kty must be "RSA", got "${String(privateJwk.kty)}"`)}if(!privateJwk.n||!privateJwk.e||!privateJwk.d){throw new Error("Rs256AuditSigner: privateJwk must include `n`, `e`, and `d` (RSA private key)")}this.privateJwk={...privateJwk};this.privateExponent=privateJwk.d;this.keyId=keyId}sign(record){const canonicalBytes=new TextEncoder().encode(canonicalizeRecordForSigning({...record,signature:"",signatureKeyId:this.keyId,signatureAlg:"RS256"}));const signature=(0,import_utils7.rsaSignBase64Url)("RS256",canonicalBytes,this.privateJwk);return{signature,keyId:this.keyId,alg:"RS256"}}getKeyId(){return this.keyId}getAlg(){return"RS256"}deriveKey(info,length){return deriveFromKeyMaterial((0,import_utils7.base64urlDecode)(this.privateExponent),info,length)}};var defaultAuditSignatureVerifier=({alg,keyId,data,signatureBase64Url,trustedKeys})=>{const trusted=trustedKeys.find(k=>k.keyId===keyId&&k.alg===alg);if(!trusted)return false;if(alg==="HS256"){if(!trusted.secret)return false;const expected=(0,import_utils7.hmacSha256)(trusted.secret,data);let actual;try{actual=(0,import_utils7.base64urlDecode)(signatureBase64Url)}catch{return false}if(actual.length!==expected.length)return false;return(0,import_utils7.timingSafeEqual)(actual,expected)}if(alg==="RS256"){if(!trusted.publicKeyPem&&!trusted.publicJwk)return false;let publicJwk;try{publicJwk=trusted.publicJwk??(0,import_utils7.pemToPublicJwk)(trusted.publicKeyPem)}catch{return false}let sigBytes;try{sigBytes=(0,import_utils7.base64urlDecode)(signatureBase64Url)}catch{return false}return(0,import_utils7.rsaVerifySync)("RS256",data,publicJwk,sigBytes)}const _exhaustive=alg;void _exhaustive;return false};var MemoryAuditStore=class{records=[];seq=0;async nextSequence(){this.seq+=1;return this.seq}async appendAtSequence(record){if(this.records.some(r=>r.sequence===record.sequence)){throw new Error(`MemoryAuditStore: record at sequence ${record.sequence} already exists`)}this.records.push(record)}async tail(){if(this.records.length===0)return void 0;return this.records[this.records.length-1]}async releaseSequence(sequence){if(sequence===this.seq&&!this.records.some(record=>record.sequence===sequence))this.seq-=1}async read(opts){const from=opts?.from??1;const limit=opts?.limit;const filtered=this.records.filter(r=>r.sequence>=from).sort((a,b)=>a.sequence-b.sequence);return limit!==void 0?filtered.slice(0,limit):filtered}};var StorageAdapterAuditStore=class{constructor(adapter,options={}){this.adapter=adapter;this.options=options}adapter;options;get sequenceKey(){return this.options.sequenceKey??SKILL_AUDIT_KEYS.sequence}recordKey(sequence){const prefix=this.options.recordKeyPrefix;return prefix?`${prefix}${sequence}`:SKILL_AUDIT_KEYS.record(sequence)}async nextSequence(){return this.adapter.incr(this.sequenceKey)}async releaseSequence(sequence){if(await this.adapter.get(this.recordKey(sequence))!==null)return;if(await this.adapter.decr(this.sequenceKey)!==sequence-1)await this.adapter.incr(this.sequenceKey)}async appendAtSequence(record){const key=this.recordKey(record.sequence);const existing=await this.adapter.get(key);if(existing!==null){throw new Error(`StorageAdapterAuditStore: record at sequence ${record.sequence} already exists`)}const payload=JSON.stringify(record);await this.adapter.set(key,payload,{ifNotExists:true});const stored=await this.adapter.get(key);if(stored!==payload){throw new Error(`StorageAdapterAuditStore: record at sequence ${record.sequence} was clobbered or not stored`)}}async tail(){const seqRaw=await this.adapter.get(this.sequenceKey);if(!seqRaw)return void 0;const seq=Number.parseInt(seqRaw,10);if(!Number.isFinite(seq)||seq<=0)return void 0;const maxLookback=this.options.tailLookback??1024;for(let s=seq;s>0&&s>seq-maxLookback;s--){const raw=await this.adapter.get(this.recordKey(s));if(!raw)continue;try{return JSON.parse(raw)}catch{throw new Error(`StorageAdapterAuditStore.tail: corrupt record at sequence ${s} cannot be parsed as JSON`)}}return void 0}async read(opts){const seqRaw=await this.adapter.get(this.sequenceKey);if(!seqRaw)return[];const head=Number.parseInt(seqRaw,10);if(!Number.isFinite(head)||head<=0)return[];const from=Math.max(1,opts?.from??1);const limit=opts?.limit;const out=[];for(let s=from;s<=head;s++){const raw=await this.adapter.get(this.recordKey(s));if(!raw)continue;try{const parsed=JSON.parse(raw);out.push(parsed);if(limit!==void 0&&out.length>=limit)break}catch{}}return out}};var import_utils8=require("@frontmcp/utils");function createSkillAuditMetrics(telemetry){const writeFailures=telemetry.createCounter("frontmcp_skills_audit_write_failures_total","Skill audit records that failed to sign or append, partitioned by reason.");const dropped=telemetry.createCounter("frontmcp_skills_audit_dropped_total","Skill audit records dropped before they were written, partitioned by reason.");return{incrementWriteFailure:reason=>writeFailures.inc(1,{reason}),incrementDropped:reason=>dropped.inc(1,{reason})}}var SKILL_AUDIT_ERROR_MESSAGE_MAX=500;var SKILL_AUDIT_QUEUE_MAX=1e3;var SUBJECT_HASH_KEY_INFO="frontmcp:audit:subject";var SUBJECT_HASH_KEY_LENGTH=32;var SkillAuditWriter=class{constructor(store,signer,logger,metrics,options={}){this.store=store;this.signer=signer;this.logger=logger;this.metrics=metrics;this.subjectMode=options.subjectMode??"hash";if(this.subjectMode==="hash"){this.subjectHashKey=options.subjectHashSecret??signer.deriveKey?.(SUBJECT_HASH_KEY_INFO,SUBJECT_HASH_KEY_LENGTH);if(!this.subjectHashKey){logger.warn(`[skill-audit] subjectMode 'hash' needs a key, and the signer (keyId "${signer.getKeyId()}") has no key material to derive one from, so subjects are recorded as 'redacted'. Set skillsConfig.audit.subjectHashSecret to record subject hashes.`)}}this.maxQueueDepth=options.maxQueueDepth??SKILL_AUDIT_QUEUE_MAX}store;signer;logger;metrics;chainHeadLock=Promise.resolve();queueDepth=0;subjectMode;subjectHashKey;maxQueueDepth;writeAuthorityPass(ctx){return this.write({phase:"authority-check-pass",ctx})}writeAuthorityFail(ctx,extras){return this.write({phase:"authority-check-fail",ctx,error:extras.reason})}writeHttpCallSuccess(ctx,extras){return this.write({phase:"http-call-success",ctx,status:extras.status,output:extras.output})}writeHttpCallFailure(ctx,extras){return this.write({phase:"http-call-failure",ctx,status:extras.status,error:extras.error})}async flush(){while(this.queueDepth>0){await this.chainHeadLock}}write(args){if(this.queueDepth>=this.maxQueueDepth){this.logger.warn(`[skill-audit] queue overflow (${this.queueDepth}/${this.maxQueueDepth}); dropping ${args.phase} record. Audit backend likely unhealthy.`);this.metrics?.incrementDropped?.("queue-overflow");return Promise.resolve()}this.queueDepth+=1;const next=this.chainHeadLock.then(()=>this.doWrite(args));this.chainHeadLock=next.catch(()=>void 0).finally(()=>{this.queueDepth-=1});return next.catch(()=>void 0)}async doWrite(args){const{phase,ctx,status,output,error}=args;let prev;try{prev=await this.store.tail()}catch(e){this.logger.warn(`[skill-audit] failed to load chain tail: ${e.message}`);this.metrics?.incrementWriteFailure("append");return}let inputHash;try{inputHash=(0,import_utils8.sha256Hex)(canonicalize(ctx.input??null))}catch(e){this.logger.warn(`[skill-audit] failed to canonicalize input: ${e.message}`);this.metrics?.incrementWriteFailure("unexpected");return}let sequence;try{sequence=await this.store.nextSequence()}catch(e){this.logger.warn(`[skill-audit] failed to allocate sequence: ${e.message}`);this.metrics?.incrementWriteFailure("append");return}const partial={id:(0,import_utils8.randomUUID)(),sequence,timestamp:new Date().toISOString(),subject:this.applySubjectMode(ctx.subject||"anonymous"),skillId:ctx.skillId,actionId:ctx.actionId,bundleId:ctx.bundleId,bundleVersion:ctx.bundleVersion,phase,inputHash,...status!==void 0?{status}:{},...phase==="http-call-success"?{outputHash:this.safeHash(output)}:{},...phase==="http-call-failure"||phase==="authority-check-fail"?{errorMessage:this.formatError(error)}:{}};const linked=linkRecord(prev,partial);let signed;try{const sig=this.signer.sign(linked);signed={...linked,signature:sig.signature,signatureKeyId:sig.keyId,signatureAlg:sig.alg}}catch(e){this.logger.warn(`[skill-audit] failed to sign record at seq=${sequence}: ${e.message}`);this.metrics?.incrementWriteFailure("sign");await this.releaseUnusedSequence(sequence);return}try{await this.store.appendAtSequence(signed);this.logger.debug?.(`[skill-audit] appended seq=${sequence} phase=${phase}`)}catch(e){this.logger.warn(`[skill-audit] failed to append record at seq=${sequence}: ${e.message}`);this.metrics?.incrementWriteFailure("append");await this.releaseUnusedSequence(sequence)}}async releaseUnusedSequence(allocated){if(!this.store.releaseSequence)return;try{await this.store.releaseSequence(allocated);this.logger.debug?.(`[skill-audit] rolled back unused sequence ${allocated}`)}catch{}}applySubjectMode(subject){if(this.subjectMode==="plain")return subject;if(this.subjectMode==="omit"||!this.subjectHashKey)return"redacted";const mac=(0,import_utils8.hmacSha256)(this.subjectHashKey,new TextEncoder().encode(subject));const hex=(0,import_utils8.bytesToHex)(mac);return`hashed:${hex.slice(0,32)}`}safeHash(value){try{return(0,import_utils8.sha256Hex)(canonicalize(value??null))}catch{return(0,import_utils8.sha256Hex)("audit:non-canonicalizable-output")}}formatError(error){let raw;if(error instanceof Error){raw=error.message}else if(typeof error==="string"){raw=error}else if(error===void 0||error===null){raw=""}else{try{raw=JSON.stringify(error)}catch{raw=String(error)}}if(raw.length>SKILL_AUDIT_ERROR_MESSAGE_MAX){return raw.slice(0,SKILL_AUDIT_ERROR_MESSAGE_MAX)+"..."}return raw}};var SkillAuditWriterToken=Symbol.for("frontmcp:SKILL_AUDIT_WRITER");var import_sdk=require("@frontmcp/sdk");var SPEC_ID_RE="[A-Za-z0-9][A-Za-z0-9_.-]*";var OPERATION_ID_RE="[A-Za-z_][A-Za-z0-9_]*";var TRAILING_BOUNDARY=String.raw`(?![A-Za-z0-9_\-])(?!\.[A-Za-z0-9_])`;var REFERENCE_RE=new RegExp(String.raw`(?<![A-Za-z0-9])op:\/\/(${SPEC_ID_RE})\/(${OPERATION_ID_RE})${TRAILING_BOUNDARY}`+`|`+String.raw`\[\[op:\s?(${SPEC_ID_RE})\/(${OPERATION_ID_RE})\]\]`,"g");function extractOpReferences(markdown){if(typeof markdown!=="string"||markdown.length===0)return[];const refs=[];const lineStarts=computeLineStarts(markdown);REFERENCE_RE.lastIndex=0;let match;while((match=REFERENCE_RE.exec(markdown))!==null){const offset=match.index;const[raw,uriSpec,uriOp,wikiSpec,wikiOp]=match;const isWikilink=wikiSpec!==void 0&&wikiOp!==void 0;const spec=isWikilink?wikiSpec:uriSpec;const operationId=isWikilink?wikiOp:uriOp;refs.push({spec,operationId,syntax:isWikilink?"wikilink":"uri",raw,location:offsetToLocation(offset,lineStarts)})}return refs}function buildKnownOps(entries){const map=new Map;for(const entry of entries){const slash=entry.indexOf("/");if(slash<=0||slash===entry.length-1)continue;const spec=entry.slice(0,slash);const op=entry.slice(slash+1);let set=map.get(spec);if(!set){set=new Set;map.set(spec,set)}set.add(op)}return map}function validateOpReferences(refs,knownOps){const diagnostics=[];for(const ref of refs){const specOps=knownOps.get(ref.spec);if(!specOps){diagnostics.push({ref,kind:"unknown-spec",message:`Unknown OpenAPI spec "${ref.spec}" referenced as ${ref.raw}`,suggestions:Array.from(knownOps.keys()).sort()});continue}if(!specOps.has(ref.operationId)){diagnostics.push({ref,kind:"unknown-operation",message:`Unknown operationId "${ref.operationId}" in spec "${ref.spec}" (${ref.raw})`,suggestions:suggestSimilarOps(ref.operationId,specOps)})}}return diagnostics}function dedupeOpReferences(refs){const seen=new Set;const result=[];for(const ref of refs){const key=`${ref.spec}/${ref.operationId}`;if(seen.has(key))continue;seen.add(key);result.push({spec:ref.spec,operationId:ref.operationId})}return result}function computeLineStarts(source){const starts=[0];for(let i=0;i<source.length;i++){if(source.charCodeAt(i)===10)starts.push(i+1)}return starts}function offsetToLocation(offset,lineStarts){let lo=0;let hi=lineStarts.length-1;while(lo<hi){const mid=lo+hi+1>>>1;if(lineStarts[mid]<=offset)lo=mid;else hi=mid-1}const line=lo+1;const column=offset-lineStarts[lo]+1;return{line,column,offset}}function suggestSimilarOps(query,known){if(known.size===0)return[];const q=query.toLowerCase();const candidates=Array.from(known);const prefix=candidates.filter(c=>c.toLowerCase().startsWith(q));const substring=candidates.filter(c=>!prefix.includes(c)&&c.toLowerCase().includes(q));const close=candidates.filter(c=>!prefix.includes(c)&&!substring.includes(c)).filter(c=>levenshtein(q,c.toLowerCase())<=Math.max(2,Math.floor(q.length/4)));return[...prefix,...substring,...close].slice(0,3)}function levenshtein(a,b){if(a===b)return 0;if(a.length===0)return b.length;if(b.length===0)return a.length;let prev=new Array(b.length+1);let curr=new Array(b.length+1);for(let j=0;j<=b.length;j++)prev[j]=j;for(let i=1;i<=a.length;i++){curr[0]=i;for(let j=1;j<=b.length;j++){const cost=a.charCodeAt(i-1)===b.charCodeAt(j-1)?0:1;curr[j]=Math.min(curr[j-1]+1,prev[j]+1,prev[j-1]+cost)}[prev,curr]=[curr,prev]}return prev[b.length]}var import_lazy_zod3=require("@frontmcp/lazy-zod");var IDENT_RE=/^[A-Za-z][A-Za-z0-9_-]*$/;var SPEC_ID_RE2=/^[A-Za-z0-9][A-Za-z0-9_.-]*$/;var SECRET_NAME_RE=/^[A-Z][A-Z0-9_]*$/;var BINDING_NAME_RE=/^[A-Z][A-Z0-9_]*$/;var ISO_DATE_RE=/^\d{4}-\d{2}-\d{2}$/;var serverInfoSchema=import_lazy_zod3.z.object({name:import_lazy_zod3.z.string().min(1).max(128),version:import_lazy_zod3.z.string().min(1).max(64),title:import_lazy_zod3.z.string().min(1).max(256).optional()}).strict();var serverSchema=import_lazy_zod3.z.object({info:serverInfoSchema,instructions:import_lazy_zod3.z.string().max(16*1024).optional()}).strict();var runtimeSchema=import_lazy_zod3.z.object({target:import_lazy_zod3.z.literal("cloudflare-worker"),compatibilityDate:import_lazy_zod3.z.string().regex(ISO_DATE_RE,"compatibilityDate must be ISO-8601 (YYYY-MM-DD)"),compatibilityFlags:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(128)).max(32).optional()}).strict();var specSourceSchema=import_lazy_zod3.z.union([import_lazy_zod3.z.string().min(1).max(2048),import_lazy_zod3.z.object({id:import_lazy_zod3.z.string().min(1).max(128).regex(SPEC_ID_RE2,"spec id must be a URI-safe identifier"),spec:import_lazy_zod3.z.string().min(1).max(2048),baseUrl:import_lazy_zod3.z.string().url("baseUrl must be a valid URL").optional(),bindingName:import_lazy_zod3.z.string().min(1).max(64).regex(IDENT_RE).optional()}).strict()]);var specsSchema=import_lazy_zod3.z.union([import_lazy_zod3.z.string().min(1).max(2048),import_lazy_zod3.z.array(specSourceSchema).max(64)]);var skillsSchema=import_lazy_zod3.z.object({source:import_lazy_zod3.z.string().min(1).max(2048).default("./skills/"),alwaysLoad:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(128)).max(32).optional(),tags:import_lazy_zod3.z.object({include:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(64)).max(64).optional(),exclude:import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1).max(64)).max(64).optional()}).strict().optional()}).strict();var tagSchema=import_lazy_zod3.z.object({name:import_lazy_zod3.z.string().min(1).max(64),description:import_lazy_zod3.z.string().min(1).max(512).optional()}).strict();var classificationRuleSchema=import_lazy_zod3.z.object({match:import_lazy_zod3.z.string().min(1).max(256),expose:import_lazy_zod3.z.enum(["tool","resource","both"]).optional(),emits:import_lazy_zod3.z.enum(["self","parent","none"]).optional()}).strict();var classificationSchema=import_lazy_zod3.z.object({rules:import_lazy_zod3.z.array(classificationRuleSchema).max(64).optional()}).strict();var durableObjectBindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),className:import_lazy_zod3.z.string().min(1).max(128).regex(IDENT_RE),scriptName:import_lazy_zod3.z.string().min(1).max(128).optional()}).strict();var d1BindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),databaseName:import_lazy_zod3.z.string().min(1).max(256),databaseId:import_lazy_zod3.z.string().min(1).max(128)}).strict();var kvBindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),id:import_lazy_zod3.z.string().min(1).max(128)}).strict();var r2BindingSchema=import_lazy_zod3.z.object({binding:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE),bucketName:import_lazy_zod3.z.string().min(1).max(256)}).strict();var bindingsSchema=import_lazy_zod3.z.object({durableObjects:import_lazy_zod3.z.array(durableObjectBindingSchema).max(32).optional(),d1Databases:import_lazy_zod3.z.array(d1BindingSchema).max(16).optional(),kvNamespaces:import_lazy_zod3.z.array(kvBindingSchema).max(32).optional(),r2Buckets:import_lazy_zod3.z.array(r2BindingSchema).max(16).optional(),vars:import_lazy_zod3.z.record(import_lazy_zod3.z.string().min(1).max(128),import_lazy_zod3.z.string().max(4096)).optional()}).strict();var trustRootSchema=import_lazy_zod3.z.object({kid:import_lazy_zod3.z.string().min(1).max(128),publicKeySecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE)}).strict();var replayGuardSchema=import_lazy_zod3.z.object({windowSeconds:import_lazy_zod3.z.number().int().min(10).max(3600).default(300),nonceKv:import_lazy_zod3.z.string().min(1).max(128).regex(BINDING_NAME_RE)}).strict();var signingSchema=import_lazy_zod3.z.object({algorithm:import_lazy_zod3.z.enum(["ed25519","rs256"]),trustRoots:import_lazy_zod3.z.array(trustRootSchema).min(1).max(8),replay:replayGuardSchema}).strict();var fronteggAuthSchema=import_lazy_zod3.z.object({tenantResolver:import_lazy_zod3.z.enum(["subdomain","header","jwt-claim"]).default("subdomain"),audience:import_lazy_zod3.z.string().min(1).max(256),issuerSecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE)}).strict();var oauthAuthSchema=import_lazy_zod3.z.object({issuer:import_lazy_zod3.z.string().url(),audience:import_lazy_zod3.z.string().min(1).max(256),credentialsSecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE).optional()}).strict();var apiKeyAuthSchema=import_lazy_zod3.z.object({header:import_lazy_zod3.z.string().min(1).max(64).default("X-API-Key"),allowlistSecret:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE)}).strict();var authSchema=import_lazy_zod3.z.discriminatedUnion("provider",[import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("none")}).strict(),import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("frontegg"),frontegg:fronteggAuthSchema}).strict(),import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("oauth"),oauth:oauthAuthSchema}).strict(),import_lazy_zod3.z.object({provider:import_lazy_zod3.z.literal("apiKey"),apiKey:apiKeyAuthSchema}).strict()]);var secretSchema=import_lazy_zod3.z.object({name:import_lazy_zod3.z.string().min(1).max(128).regex(SECRET_NAME_RE),required:import_lazy_zod3.z.boolean().default(true),description:import_lazy_zod3.z.string().min(1).max(512).optional()}).strict();var environmentOverlaySchema=import_lazy_zod3.z.object({server:serverSchema.partial().optional(),specs:specsSchema.optional(),skills:skillsSchema.partial().optional(),classification:classificationSchema.optional(),bindings:bindingsSchema.optional(),auth:authSchema.optional(),vars:import_lazy_zod3.z.record(import_lazy_zod3.z.string().min(1).max(128),import_lazy_zod3.z.string().max(4096)).optional()}).strict();var deployManifestSchema=import_lazy_zod3.z.object({$schema:import_lazy_zod3.z.string().url().optional(),version:import_lazy_zod3.z.literal(1),name:import_lazy_zod3.z.string().min(1).max(128).regex(IDENT_RE),runtime:runtimeSchema,server:serverSchema,specs:specsSchema,skills:skillsSchema.default({source:"./skills/"}),tags:import_lazy_zod3.z.array(tagSchema).max(64).optional(),classification:classificationSchema.optional(),bindings:bindingsSchema,signing:signingSchema,auth:authSchema,secrets:import_lazy_zod3.z.array(secretSchema).max(64).optional(),environments:import_lazy_zod3.z.record(import_lazy_zod3.z.string().min(1).max(64).regex(IDENT_RE),environmentOverlaySchema).optional()}).strict();function applyEnvironmentOverlay(base,overlay){const effectiveBindings=overlay.bindings??base.bindings;const mergedBindings=overlay.vars!==void 0?{...effectiveBindings,vars:{...effectiveBindings.vars??{},...overlay.vars}}:effectiveBindings;return{...base,server:overlay.server?{...base.server,...overlay.server}:base.server,specs:overlay.specs??base.specs,skills:overlay.skills?{...base.skills,...overlay.skills}:base.skills,classification:overlay.classification??base.classification,bindings:mergedBindings,auth:overlay.auth??base.auth}}function validateEffectiveManifest(manifest,label,errors){const declaredSecrets=new Set((manifest.secrets??[]).map(s=>s.name));const refs=[];if(manifest.auth.provider==="frontegg"){refs.push({where:"auth.frontegg.issuerSecret",name:manifest.auth.frontegg.issuerSecret})}else if(manifest.auth.provider==="oauth"&&manifest.auth.oauth.credentialsSecret){refs.push({where:"auth.oauth.credentialsSecret",name:manifest.auth.oauth.credentialsSecret})}else if(manifest.auth.provider==="apiKey"){refs.push({where:"auth.apiKey.allowlistSecret",name:manifest.auth.apiKey.allowlistSecret})}for(const root of manifest.signing.trustRoots){refs.push({where:`signing.trustRoots[kid=${root.kid}].publicKeySecret`,name:root.publicKeySecret})}for(const ref of refs){if(!declaredSecrets.has(ref.name)){errors.push(`${label}Secret "${ref.name}" referenced at ${ref.where} is not declared in secrets[]`)}}const declaredKv=new Set((manifest.bindings.kvNamespaces??[]).map(kv=>kv.binding));if(!declaredKv.has(manifest.signing.replay.nonceKv)){errors.push(`${label}signing.replay.nonceKv "${manifest.signing.replay.nonceKv}" does not match any bindings.kvNamespaces[].binding`)}for(const id of manifest.skills.alwaysLoad??[]){if(!/^[a-z0-9][a-z0-9-]*$/.test(id)){errors.push(`${label}skills.alwaysLoad entry "${id}" is not a valid kebab-case skill id`)}}if(manifest.tags){const declaredTags=new Set(manifest.tags.map(t=>t.name));const tagFilter=manifest.skills.tags;for(const t of[...tagFilter?.include??[],...tagFilter?.exclude??[]]){if(!declaredTags.has(t)){errors.push(`${label}skills.tags references unknown tag "${t}" (not in tags[])`)}}}}function crossValidateManifest(manifest){const errors=[];validateEffectiveManifest(manifest,"",errors);for(const[envName,overlay]of Object.entries(manifest.environments??{})){const effective=applyEnvironmentOverlay(manifest,overlay);validateEffectiveManifest(effective,`environments.${envName}: `,errors)}return errors.length===0?{ok:true}:{ok:false,errors}}function specStem(ref){const noQuery=ref.split("?")[0].split("#")[0];const base=noQuery.split("/").pop()??noQuery;const stem=base.replace(/\.(ya?ml|json)$/i,"");const safe=(stem||base).replace(/[^a-zA-Z0-9_.-]/g,"-");return safe||"spec"}function normalizeSpec(source){if(typeof source==="string"){const id=specStem(source);return{id,spec:source,bindingName:id}}return{id:source.id,spec:source.spec,baseUrl:source.baseUrl,bindingName:source.bindingName??source.id}}function buildFrontMcpConfigFromManifest(manifest){const info={name:manifest.server.info.name,version:manifest.server.info.version,...manifest.server.info.title!==void 0?{title:manifest.server.info.title}:{}};const skills={source:manifest.skills.source,...manifest.skills.alwaysLoad?{alwaysLoad:manifest.skills.alwaysLoad}:{},...manifest.skills.tags?{tags:manifest.skills.tags}:{}};let specs=[];let specsDir;if(typeof manifest.specs==="string"){specsDir=manifest.specs}else{specs=manifest.specs.map(normalizeSpec);const seen=new Set;for(const s of specs){if(seen.has(s.id)){throw new Error(`manifest specs: duplicate spec id "${s.id}" (derived from the filename stem). Give the colliding spec an explicit { id } in frontmcp.deploy.yaml.`)}seen.add(s.id)}}return{info,...manifest.server.instructions!==void 0?{instructions:manifest.server.instructions}:{},skillsConfig:{enabled:true,injectInstructions:"append"},skills,specs,...specsDir!==void 0?{specsDir}:{}}}function classifyOperations(specId,ops){if(!specId||specId.length===0){throw new Error("classifyOperations: specId is required")}if(!Array.isArray(ops))return[];const pathsWithGet=new Set;for(const op of ops){if(op&&typeof op.path==="string"&&typeof op.method==="string"&&op.method.toUpperCase()==="GET"){pathsWithGet.add(op.path)}}return ops.map(op=>classifyOne(specId,op,pathsWithGet))}function classifyOne(specId,op,pathsWithGet){const method=normaliseMethod(op.method);const path=op.path;const hasTerminalParam=pathHasTerminalParam(path);const parent=parentPath(path);const thisIsResource=pathsWithGet.has(path);const parentIsResource=parent!==null&&pathsWithGet.has(parent);const selfUri=pathToResourceUri(specId,path);const parentUri=parent!==null?pathToResourceUri(specId,parent):null;if(method==="GET"){if(hasTerminalParam){return{operationId:op.operationId,method,path,specId,expose:"both",resourceUriTemplate:selfUri}}return{operationId:op.operationId,method,path,specId,expose:"resource",resourceUriTemplate:selfUri}}const base={operationId:op.operationId,method,path,specId,expose:"tool"};if(method==="POST"){if(thisIsResource&&!hasTerminalParam){return{...base,emit:{kind:"listChanged",pathTemplate:path,resourceUriTemplate:selfUri}}}if(thisIsResource&&hasTerminalParam){return{...base,emit:{kind:"updated",pathTemplate:path,resourceUriTemplate:selfUri}}}if(parentIsResource&&parentUri!==null&&parent!==null){return{...base,emit:{kind:"updated",pathTemplate:parent,resourceUriTemplate:parentUri}}}return base}if(method==="PUT"||method==="PATCH"){if(thisIsResource){return{...base,emit:{kind:"updated",pathTemplate:path,resourceUriTemplate:selfUri}}}if(parentIsResource&&parentUri!==null&&parent!==null){return{...base,emit:{kind:"updated",pathTemplate:parent,resourceUriTemplate:parentUri}}}return base}if(method==="DELETE"){if(hasTerminalParam&&parentIsResource&&parentUri!==null&&parent!==null){return{...base,emit:{kind:"listChanged",pathTemplate:parent,resourceUriTemplate:parentUri}}}if(!hasTerminalParam&&thisIsResource){return{...base,emit:{kind:"listChanged",pathTemplate:path,resourceUriTemplate:selfUri}}}return base}return base}function applyClassificationOverrides(classified,rules){if(!Array.isArray(rules)||rules.length===0)return classified.slice();const compiled=rules.map(rule=>({rule,matcher:compileMatcher(rule.match)}));return classified.map(op=>{for(const{rule,matcher}of compiled){if(!matcher(op))continue;return applyOne(op,rule)}return op})}var KNOWN_METHODS=new Set(["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS","TRACE"]);function normaliseMethod(m){const up=(m??"").toUpperCase();return KNOWN_METHODS.has(up)?up:up}function pathHasTerminalParam(path){if(typeof path!=="string"||path.length===0)return false;const segments=path.split("/").filter(s=>s.length>0);if(segments.length===0)return false;const last=segments[segments.length-1];return last.startsWith("{")&&last.endsWith("}")&&last.length>=3}function parentPath(path){if(typeof path!=="string"||path.length===0)return null;const segments=path.split("/").filter(s=>s.length>0);if(segments.length<=1)return null;return"/"+segments.slice(0,-1).join("/")}function pathToResourceUri(specId,path){const cleaned=path.startsWith("/")?path.slice(1):path;return`mcp+op://${specId}/${cleaned}`}function compileMatcher(match){const trimmed=(match??"").trim();const sep2=trimmed.indexOf(" ");if(sep2<=0||sep2===trimmed.length-1){return()=>false}const methodPattern=trimmed.slice(0,sep2).toUpperCase();const pathPattern=trimmed.slice(sep2+1);const pathRe=globToRegExp(pathPattern);return op=>{if(methodPattern!=="*"&&methodPattern!==op.method)return false;return pathRe.test(op.path)}}function globToRegExp(glob){const STAR_STAR="\0";const STAR="";const replaced=glob.replace(/\*\*/g,STAR_STAR).replace(/\*/g,STAR);const escaped=replaced.replace(/[.+^${}()|[\]\\]/g,"\\$&");const expanded=escaped.split(STAR_STAR).join(".*").split(STAR).join("[^/]*");return new RegExp(`^${expanded}$`)}function defaultEmitKindForMethod(method){return method==="DELETE"?"listChanged":"updated"}function applyOne(op,rule){let next={...op};if(rule.expose!==void 0){next={...next,expose:rule.expose}}if(rule.emits!==void 0){if(rule.emits==="none"){const{emit:_drop,...rest}=next;next=rest}else if(rule.emits==="self"){const target=pathToResourceUri(op.specId,op.path);next={...next,emit:{kind:next.emit?.kind??defaultEmitKindForMethod(op.method),pathTemplate:op.path,resourceUriTemplate:target}}}else if(rule.emits==="parent"){const parent=parentPath(op.path);if(parent!==null){next={...next,emit:{kind:next.emit?.kind??defaultEmitKindForMethod(op.method),pathTemplate:parent,resourceUriTemplate:pathToResourceUri(op.specId,parent)}}}else{const{emit:_drop,...rest}=next;next=rest}}}return next}var TEMPLATE_PLACEHOLDER_RE=/\{([^{}]+)\}/g;function renderResourceUri(template,args){if(typeof template!=="string"||template.length===0){return{ok:false,missing:[]}}const argObj=args!==null&&typeof args==="object"?args:{};const missing=[];const uri=template.replace(TEMPLATE_PLACEHOLDER_RE,(_match,name)=>{const value=argObj[name];if(value===void 0||value===null){missing.push(name);return""}if(typeof value==="object"){missing.push(name);return""}if(typeof value==="number"&&!Number.isFinite(value)){missing.push(name);return""}return encodeURIComponent(String(value))});if(missing.length>0)return{ok:false,missing};return{ok:true,uri}}function buildResourceChangeNotification(classification,args){const emit=classification.emit;if(!emit)return{notification:null,reason:"no-emit"};if(emit.kind==="listChanged"){const rendered2=renderResourceUri(emit.resourceUriTemplate,args);if(!rendered2.ok){return{notification:null,reason:"unresolved-template",missing:rendered2.missing}}return{notification:{method:"notifications/resources/list_changed"}}}const rendered=renderResourceUri(emit.resourceUriTemplate,args);if(!rendered.ok){return{notification:null,reason:"unresolved-template",missing:rendered.missing}}return{notification:{method:"notifications/resources/updated",params:{uri:rendered.uri}}}}var ClassificationRegistry=class{byToolName=new Map;register(toolName,classification){if(typeof toolName!=="string"||toolName.length===0){throw new Error("ClassificationRegistry.register: toolName must be a non-empty string")}const prev=this.byToolName.get(toolName);this.byToolName.set(toolName,classification);return prev}registerAll(classifications){let added=0;let replaced=0;for(const c of classifications){const toolName=`${c.specId}.${c.operationId}`;const prev=this.byToolName.get(toolName);this.byToolName.set(toolName,c);if(prev)replaced++;else added++}return{added,replaced}}lookup(toolName){return this.byToolName.get(toolName)}unregister(toolName){return this.byToolName.delete(toolName)}clear(){this.byToolName.clear()}size(){return this.byToolName.size}getAll(){const out=[];for(const[toolName,classification]of this.byToolName.entries()){out.push({toolName,classification})}return out}buildNotificationForCall(toolName,args){const classification=this.byToolName.get(toolName);if(!classification)return{notification:null,reason:"no-emit"};return buildResourceChangeNotification(classification,args)}};0&&(module.exports={BundlePinnedError,BundlePushJwtVerifier,BundleStore,ClassificationRegistry,FilesystemSkillsSource,Hs256AuditSigner,InlineSource,MemoryAuditStore,NpmSource,OverlayParseError,Rs256AuditSigner,SKILL_AUDIT_ERROR_MESSAGE_MAX,SKILL_AUDIT_GENESIS_PREV_HASH,SKILL_AUDIT_KEYS,SKILL_AUDIT_QUEUE_MAX,SaasPullSource,SaasPushRefusedError,SkillAuditWriter,SkillAuditWriterToken,SkillDependencyCycleError,SkillDependencyInvariantError,SkillDependencyMissingError,StaticSource,StorageAdapterAuditStore,WebhookReplayGuard,applyClassificationOverrides,applyEnvironmentOverlay,buildFrontMcpConfigFromManifest,buildKnownOps,buildResourceChangeNotification,bundleDigest,bundleSkillToActions,bundleSourceSchema,canonicalize,canonicalizeRecordForSigning,classifyOne,classifyOperations,compileSkilledBundleFromOpenApi,createBundleSource,createSkillAuditMetrics,crossValidate,crossValidateManifest,dedupeOpReferences,defaultAuditSignatureVerifier,deployManifestSchema,diffBundles,extractOpReferences,formatDiffSummary,hasSkillAuditFactory,linkRecord,loadFilesystemSkill,nextPrevHash,npmSourceSchema,parseOverlay,parseSkillFrontmatter,renderResourceUri,resolveSkillLoadOrder,resolvedBundleSchema,saasSourceSchema,setSkillAuditFactory,signatureKeySchema,staticSourceSchema,validateOpReferences,verifyBundleSignature,verifyChain});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-skilled-bundle.d.ts","sourceRoot":"","sources":["../../../src/skills/openapi/compile-skilled-bundle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compile-skilled-bundle.d.ts","sourceRoot":"","sources":["../../../src/skills/openapi/compile-skilled-bundle.ts"],"names":[],"mappings":"AAqBA,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAOD,oGAAoG;AACpG,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,CAAC,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,YAAY,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,CAChB,MAAM,EACN;QACE,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,WAAW,EAAE,OAAO,CAAC;QACrB,YAAY,EAAE,OAAO,CAAC;QACtB,MAAM,EAAE,OAAO,EAAE,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;KACxB,CACF,CAAC;CACH;AAED;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,EAAE,EACzB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAiEhC"}
|
|
@@ -22,7 +22,7 @@ export interface PushJwtVerifyResult {
|
|
|
22
22
|
reason?: string;
|
|
23
23
|
payload?: Record<string, unknown>;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/** For a host serving its own push endpoint: `saas` with `enableWebhook` checks its pushes as it checks the pull token. */
|
|
26
26
|
export declare class BundlePushJwtVerifier {
|
|
27
27
|
private readonly options;
|
|
28
28
|
private readonly logger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-verifier.d.ts","sourceRoot":"","sources":["../../../src/skills/security/jwt-verifier.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAA6C,MAAM,gBAAgB,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,WAAW,4BAA4B;IAC3C,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAID,
|
|
1
|
+
{"version":3,"file":"jwt-verifier.d.ts","sourceRoot":"","sources":["../../../src/skills/security/jwt-verifier.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAA6C,MAAM,gBAAgB,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,WAAW,4BAA4B;IAC3C,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAID,2HAA2H;AAC3H,qBAAa,qBAAqB;IAK9B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;gBAGhB,OAAO,EAAE,4BAA4B,EACrC,MAAM,EAAE,cAAc,EACvC,IAAI,CAAC,EAAE,WAAW;IAUpB;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAsDzD,OAAO,CAAC,UAAU;CAQnB"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/** Why a check refused: also the `reason` label of `frontmcp_skills_replay_rejects_total`. */
|
|
2
|
+
export type ReplayRejectCode = 'invalid_timestamp' | 'invalid_nonce' | 'outside_window' | 'nonce_replay';
|
|
1
3
|
export interface ReplayCheckResult {
|
|
2
4
|
ok: boolean;
|
|
3
5
|
reason?: string;
|
|
6
|
+
code?: ReplayRejectCode;
|
|
4
7
|
}
|
|
5
8
|
/**
|
|
6
9
|
* Minimal telemetry surface for replay rejects. Structurally compatible
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-replay-guard.d.ts","sourceRoot":"","sources":["../../../src/skills/security/webhook-replay-guard.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook-replay-guard.d.ts","sourceRoot":"","sources":["../../../src/skills/security/webhook-replay-guard.ts"],"names":[],"mappings":"AAMA,8FAA8F;AAC9F,MAAM,MAAM,gBAAgB,GAAG,mBAAmB,GAAG,eAAe,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAEzG,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7D;AACD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAAC;CACvE;AAED,MAAM,WAAW,yBAAyB;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,oBAAoB,CAAC;CAClC;AAKD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;gBAEnD,OAAO,GAAE,yBAA8B;IAyBnD,2CAA2C;IAC3C,cAAc,CAAC,EAAE,EAAE,MAAM,MAAM,GAAG,IAAI;IAItC;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,iBAAiB;IA0DtE,iEAAiE;IACjE,KAAK,IAAI,MAAM;IAYf,mDAAmD;IACnD,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-options.d.ts","sourceRoot":"","sources":["../../src/skills/source-options.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AAkBvC,eAAO,MAAM,kBAAkB;;;;gCAM7B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;gCAiB1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"source-options.d.ts","sourceRoot":"","sources":["../../src/skills/source-options.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AAkBvC,eAAO,MAAM,kBAAkB;;;;gCAM7B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;gCAiB1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;gCA2B3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;gCAS7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;0CAK7B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AACjE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACrE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAIrE,eAAO,MAAM,kBAAkB;;;;;;;gCAO7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
|
@@ -2,10 +2,10 @@ import type { FrontMcpLogger } from '@frontmcp/sdk';
|
|
|
2
2
|
import type { BundleSourceOptions } from '../source-options';
|
|
3
3
|
import { InlineSource } from './inline.source';
|
|
4
4
|
import { NpmSource } from './npm.source';
|
|
5
|
-
import { SaasPullSource } from './saas-pull.source';
|
|
5
|
+
import { SaasPullSource, SaasPushRefusedError } from './saas-pull.source';
|
|
6
6
|
import type { BundleSourceDeps, SkillBundleSource } from './skill-bundle-source.interface';
|
|
7
7
|
import { StaticSource } from './static.source';
|
|
8
|
-
export { StaticSource, NpmSource, SaasPullSource, InlineSource };
|
|
9
|
-
export type { BundleCacheStore, BundleSourceDeps, BundleSourceListener, SkillBundleSource, } from './skill-bundle-source.interface';
|
|
8
|
+
export { StaticSource, NpmSource, SaasPullSource, SaasPushRefusedError, InlineSource };
|
|
9
|
+
export type { BundleCacheStore, BundlePushReceiver, BundleSourceDeps, BundleSourceListener, SkillBundleSource, } from './skill-bundle-source.interface';
|
|
10
10
|
export declare function createBundleSource(source: BundleSourceOptions, cacheDir: string | undefined, logger: FrontMcpLogger, deps?: BundleSourceDeps): SkillBundleSource;
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,oBAAoB,EAAE,YAAY,EAAE,CAAC;AACvF,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EAGtB,IAAI,CAAC,EAAE,gBAAgB,GACtB,iBAAiB,CAWnB"}
|