@interactive-inc/claude-funnel 0.68.1 → 0.68.2

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.
@@ -1,3 +1,3 @@
1
1
  import { a as queryRows, i as previewOf, n as diagnosticEventOfProcessed, o as toDiagnosticConnectionError, r as diagnosticEventOfRaw, s as toDiagnosticEvent, t as diagnosticConnectionEventOf } from "./diagnostic-event-CxMM5Bl2.js";
2
- import { t as FunnelDiagnostics } from "./funnel-diagnostics-7rV3HxY3.js";
2
+ import { t as FunnelDiagnostics } from "./funnel-diagnostics-DremV1of.js";
3
3
  export { FunnelDiagnostics, diagnosticConnectionEventOf, diagnosticEventOfProcessed, diagnosticEventOfRaw, previewOf, queryRows, toDiagnosticConnectionError, toDiagnosticEvent };
@@ -48,12 +48,6 @@ const buildDiagnosis = (report, unresolvedErrors = report.connectionErrors) => {
48
48
  nextActions: ["fnl gateway restart"],
49
49
  rootCause: "supervisor missing listeners declared in settings.json"
50
50
  };
51
- if (report.configuredConnectors === 0) return {
52
- status: "warn",
53
- message: "no connectors configured on this channel",
54
- nextActions: [`fnl channels ${channel} connectors add <name> --type=slack ...`],
55
- rootCause: null
56
- };
57
51
  const authFailed = unresolvedErrors.filter((e) => e.status === "auth-failed");
58
52
  if (authFailed.length > 0) {
59
53
  const detail = authFailed[authFailed.length - 1]?.detail ?? null;
@@ -64,7 +58,7 @@ const buildDiagnosis = (report, unresolvedErrors = report.connectionErrors) => {
64
58
  rootCause: detail ?? "token rejected by upstream auth.test"
65
59
  };
66
60
  }
67
- const allDead = report.listeners.every((l) => !l.alive);
61
+ const allDead = report.listeners.length > 0 && report.listeners.every((l) => !l.alive);
68
62
  const someDead = report.listeners.some((l) => !l.alive);
69
63
  if (allDead) return {
70
64
  status: "error",
@@ -72,7 +72,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
72
72
  ${g}`:g,$._funnel_omitted=["blocks"]}return $},aD=(i)=>{let $=aI(i,jP);if(Array.isArray($.files))$.files=$.files.map(WP);if(Array.isArray($.attachments))$.attachments=$.attachments.map(QP);return $};var KP=new Set(["message","app_mention"]),GP=new Set([void 0,"thread_broadcast","bot_message","file_share"]),YP=1e4,wi=(i,$)=>{let n=i[$];return typeof n==="string"?n:void 0};class eI{ownBotUserId;ownBotId;minify;now;dedup=new Map;constructor(i){this.ownBotUserId=i.ownBotUserId,this.ownBotId=i.ownBotId,this.minify=i.minify??!0,this.now=i.now??(()=>Date.now())}process(i){let $=wi(i,"type");if(!$||!KP.has($))return{skip:!0,reason:"skip:type"};let n=wi(i,"subtype");if(!GP.has(n))return{skip:!0,reason:"skip:subtype"};let g=wi(i,"channel")??"",v=wi(i,"event_ts")??wi(i,"ts")??"",r=`${g}:${v}`,b=this.now();if(this.dedup.has(r))return{skip:!0,reason:"skip:dedup"};this.dedup.set(r,b);for(let z of this.dedup.keys())if((this.dedup.get(z)??0)<b-YP)this.dedup.delete(z);let u=wi(i,"user"),U=wi(i,"bot_id");if(u===this.ownBotUserId)return{skip:!0,reason:"skip:self-user"};if(U===this.ownBotId)return{skip:!0,reason:"skip:self-bot"};let I=wi(i,"text")??"",_=I.includes(`<@${this.ownBotUserId}>`),k=wi(i,"thread_ts")??wi(i,"ts")??"",O=wi(i,"ts")??"",J=$==="app_mention"?"app_mention":"message",Q=this.minify?aD(i):i;return{skip:!1,event:{kind:"message",channel:g,user:u??"",rawText:I,text:HP(I,this.ownBotUserId),threadTs:k,ts:O,isThreadRoot:k===O,mentioned:_,source:J},content:JSON.stringify(Q),meta:{event_type:"slack",channel_id:g,user_id:u??"",mentioned:String(_),thread_ts:k},shouldReact:_,channel:g,timestamp:O}}}var HP=(i,$)=>i.replace(new RegExp(`<@${$}>`,"g"),"").trim();var zP=D.object({ok:D.boolean(),user_id:D.string().optional(),bot_id:D.string().optional(),error:D.string().optional()}),SP="https://slack.com/api/auth.test";class sI extends j${config;env;flumeDeps;http;signal;preprocessEvent;onInteractive;processor=null;botToken="";constructor(i){super({type:"slack",connectorId:i.config.id,channelId:i.channelId??null,logger:i.logger,diagnosticLog:i.diagnosticLog});this.config=i.config,this.env=i.env??process.env,this.flumeDeps=i.flumeDeps??{},this.http=i.http??new Di,this.signal=i.signal,this.preprocessEvent=i.preprocessEvent,this.onInteractive=i.onInteractive}async start(i){this.diagnostics.recordConnection("started","");let $,n;try{$=Yi({literal:this.config.appToken,envVar:this.config.appTokenEnv,env:this.env,label:`${this.config.name}.appToken`}),n=Yi({literal:this.config.botToken,envVar:this.config.botTokenEnv,env:this.env,label:`${this.config.name}.botToken`})}catch(r){throw this.diagnostics.recordConnection("auth-failed",E(r)),r}this.botToken=n;let g=await this.callAuthTest();if(!g.ok){let r=g.error??"auth.test returned ok=false";throw this.diagnostics.recordConnection("auth-failed",r),new $n(this.config.name,r)}this.processor=new eI({ownBotUserId:g.user_id??"",ownBotId:g.bot_id??"",minify:this.config.minify});let v=new dD({appToken:$,botToken:this.botToken});await this.runStart({source:v,onLog:s$(this.logger),deps:e$(this.flumeDeps),signal:this.signal,onEvent:(r)=>{if(r.source!=="slack")return Promise.resolve();return this.handleEvent(r,i)}})}onStop(){this.processor=null}async handleInteractive(i){let $=crypto.randomUUID(),n=JSON.stringify(i);if(this.diagnostics.recordRaw($,n),!this.onInteractive){this.diagnostics.recordProcessed($,"skip:no-interactive-handler","");return}let g=typeof i.type==="string"?i.type:"unknown";try{await this.onInteractive(i),this.diagnostics.recordProcessed($,`interactive:${g}`,"")}catch(v){let r=E(v);this.diagnostics.recordProcessed($,`interactive-error:${g}`,r),this.logger?.error(`slack interactive handler error (${g})`,{error:r})}}async callAuthTest(){let i;try{i=await(await this.http.fetch({method:"POST",url:SP,headers:{Authorization:`Bearer ${this.botToken}`,"Content-Type":"application/x-www-form-urlencoded"}})).text()}catch(n){throw this.diagnostics.recordConnection("error",E(n)),n}let $=zP.safeParse(pD(i));if(!$.success)return{ok:!1,error:`non-JSON auth.test response: ${i.slice(0,200)}`};return $.data}async handleEvent(i,$){if(!this.processor)return;if(i.type==="interactive"){await this.handleInteractive(i.data);return}let n=i.data.event;if(!AP(n)){let u=crypto.randomUUID();this.diagnostics.recordRaw(u,JSON.stringify(i.data)),this.diagnostics.recordProcessed(u,"skip:non-object-event","");return}let g=crypto.randomUUID(),v=JSON.stringify(n);this.diagnostics.recordRaw(g,v);let r=n;if(this.preprocessEvent){let u=await this.preprocessEvent(n);if(u===null){this.diagnostics.recordProcessed(g,"skip:preprocess",v);return}r=u}let b=this.processor.process(r);if(b.skip){this.diagnostics.recordProcessed(g,b.reason,v);return}await this.deliver($,g,v,b.content,b.meta,b.shouldReact)}async deliver(i,$,n,g,v,r){try{await i(g,v)}catch(b){this.diagnostics.recordProcessed($,"emitted:delivery-failed",g||n),this.logger?.error("slack notify error",{error:E(b)});return}if(this.diagnostics.recordProcessed($,"emitted",g),r)this.postReaction(v).catch((b)=>{this.diagnostics.recordProcessed($,"emitted:reaction-failed",E(b)),this.logger?.warn("slack reaction failed",{error:E(b)})})}async postReaction(i){let $=await this.http.fetch({method:"POST",url:"https://slack.com/api/reactions.add",headers:{Authorization:`Bearer ${this.botToken}`,"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({channel:i.channel_id??"",timestamp:i.thread_ts??"",name:"eyes"}).toString()}),n=await $.text(),g=lP(n);if(!g.ok)throw Error(`slack reactions.add: ${g.error??`status=${$.status}`}`)}}var AP=(i)=>typeof i==="object"&&i!==null&&!Array.isArray(i),pD=(i)=>{try{return JSON.parse(i)}catch{return null}},BP=D.object({ok:D.boolean(),error:D.string().optional()}),lP=(i)=>{let $=BP.safeParse(pD(i));if(!$.success)return{ok:!1,error:`non-JSON response: ${i.slice(0,200)}`};return $.data};var eD=(i={})=>({type:"slack",toolExposed:!0,createListener($,n){let g=Ti.parse($);return new sI({config:g,channelId:n.channelId,logger:n.logger,diagnosticLog:n.diagnosticLog,http:n.http,signal:n.signal,preprocessEvent:i.preprocessEvent,onInteractive:i.onInteractive})},createAdapter($,n){return new hI({config:Ti.parse($),http:n.http})},secretTokens($){let n=Ti.parse($);return[n.botToken,n.appToken].filter((g)=>g!==void 0)},buildConfig($,n){return Ti.parse({id:n.id,type:"slack",name:$.name,...typeof $.botToken==="string"?{botToken:$.botToken}:{},...typeof $.appToken==="string"?{appToken:$.appToken}:{},...typeof $.botTokenEnv==="string"?{botTokenEnv:$.botTokenEnv}:{},...typeof $.appTokenEnv==="string"?{appTokenEnv:$.appTokenEnv}:{},minify:typeof $.minify==="boolean"?$.minify:!0,createdAt:n.now,updatedAt:n.now})},applyUpdate($,n,g){let v=Ti.parse($);return Ti.parse({id:v.id,name:v.name,type:"slack",minify:v.minify,createdAt:v.createdAt,updatedAt:g.now,...O$("botToken","botTokenEnv",n,v),...O$("appToken","appTokenEnv",n,v)})},operations:{}});var sD=()=>[eD(),LD(),zD(),mD()];import{homedir as i_}from"os";import{dirname as vk,join as $_}from"path";class vn{}class Zi extends vn{generate(){return crypto.randomUUID()}}class nn{}var ik=D.object({id:D.string(),name:D.string(),type:D.string(),createdAt:D.string().datetime().optional(),updatedAt:D.string().datetime().optional()});var LP=ik.loose(),TP=D.enum(["fanout","exclusive"]),ZP=D.object({id:D.string(),name:D.string(),delivery:TP.default("fanout"),connectors:D.array(LP).default([])}),qP=D.object({id:D.string(),name:D.string(),path:D.string(),channelId:D.string(),options:D.array(D.string()).default([]),env:D.record(D.string(),D.string()).default({}),resume:D.boolean().default(!0),sessionId:D.string().optional()}),qi=1,$k=D.object({version:D.literal(qi).default(qi),channels:D.array(ZP).default([]),profiles:D.array(qP).default([])});function ur(){let i=process.env.FUNNEL_DIR;if(i&&i.length>0)return MP(i);return $_(i_(),".funnel")}function MP(i){let $=i_(),n=i;if(n==="~")return $;if(n.startsWith("~/")||n.startsWith("~\\"))n=$+n.slice(1);return n=n.split("${HOME}").join($),n=n.split("${USERPROFILE}").join($),n.replace(/\\/g,"/")}var xP=9742;function Mi(){return Number(process.env.FUNNEL_PORT)||xP}var ji=$_(i_(),".funnel"),NP=$_(ji,"settings.json"),EP=new F,tP=new Zi;class v_ extends nn{path;fs;idGenerator;constructor(i={}){super();this.path=i.path??NP,this.fs=i.fs??EP,this.idGenerator=i.idGenerator??tP,Object.freeze(this)}read(){if(!this.fs.existsSync(this.path))return{version:qi,channels:[],profiles:[]};let i=this.fs.readFileSync(this.path),$=JSON.parse(i);if(this.looksLikeLegacy($))throw Error(`legacy settings.json detected at ${this.path}. The schema changed (channel.connectors are now nested objects with ids; profile fields renamed). Migration is intentionally not provided. Back up and remove the old file:
73
73
  mv ${this.path} ${this.path}.bak`);if($&&typeof $==="object"&&"version"in $&&$.version!==qi)throw Error(`unsupported settings.json version (${this.path}): expected ${qi}, got ${String($.version)}`);let n=this.backfillProfileIds($),g=$k.safeParse($);if(!g.success)throw Error(`invalid settings.json (${this.path}): ${g.error.issues.map((v)=>`${v.path.join(".")}: ${v.message}`).join(", ")}`);if(n)this.write(g.data);return g.data}looksLikeLegacy(i){if(!i||typeof i!=="object")return!1;let $=i;if(Array.isArray($.channels))for(let n of $.channels){if(!n||typeof n!=="object")continue;let g=n;if(Array.isArray(g.connectors)&&g.connectors.some((v)=>typeof v==="string"))return!0;if(!("id"in g)&&"name"in g)return!0}if(Array.isArray($.connectors))return!0;if(Array.isArray($.repositories))return!0;if(Array.isArray($.profiles))for(let n of $.profiles){if(!n||typeof n!=="object")continue;let g=n;if("repository"in g||"envFiles"in g||"channel"in g&&!("channelId"in g))return!0}return!1}backfillProfileIds(i){if(!i||typeof i!=="object")return!1;let $=i;if(!Array.isArray($.profiles))return!1;let n=!1;for(let g of $.profiles){if(!g||typeof g!=="object")continue;let v=g;if(typeof v.id!=="string")v.id=this.idGenerator.generate(),n=!0}return n}write(i){this.fs.mkdirSync(vk(this.path),{recursive:!0});let $={...i,version:qi};this.fs.writeSecretFileSync(this.path,`${JSON.stringify($,null,2)}
74
74
  `)}update(i){return this.fs.mkdirSync(vk(this.path),{recursive:!0}),this.fs.withFileLock(`${this.path}.lock`,()=>{let $=this.read(),n=i($);return this.write($),n})}}import{tmpdir as cP}from"os";import{join as CP}from"path";function fi(){let i=process.env.FUNNEL_TMP_DIR;if(i&&i.length>0)return i;return CP(cP(),"funnel")}import{join as wX}from"path";var g_=(i)=>{let $=i.split("/");if($[0]==="")$.shift();return $},nk=(i)=>{let{groups:$,path:n}=RP(i),g=g_(n);return FP(g,$)},RP=(i)=>{let $=[];return i=i.replace(/\{[^}]+\}/g,(n,g)=>{let v=`@${g}`;return $.push([v,n]),v}),{groups:$,path:i}},FP=(i,$)=>{for(let n=$.length-1;n>=0;n--){let[g]=$[n];for(let v=i.length-1;v>=0;v--)if(i[v].includes(g)){i[v]=i[v].replace(g,$[n][1]);break}}return i},Ur={},gk=(i,$)=>{if(i==="*")return"*";let n=i.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);if(n){let g=`${i}#${$}`;if(!Ur[g])if(n[2])Ur[g]=$&&$[0]!==":"&&$[0]!=="*"?[g,n[1],new RegExp(`^${n[2]}(?=/${$})`)]:[i,n[1],new RegExp(`^${n[2]}$`)];else Ur[g]=[i,n[1],!0];return Ur[g]}return null},$v=(i,$)=>{try{return $(i)}catch{return i.replace(/(?:%[0-9A-Fa-f]{2})+/g,(n)=>{try{return $(n)}catch{return n}})}},oP=(i)=>$v(i,decodeURI),r_=(i)=>{let $=i.url,n=$.indexOf("/",$.indexOf(":")+4),g=n;for(;g<$.length;g++){let v=$.charCodeAt(g);if(v===37){let r=$.indexOf("?",g),b=$.indexOf("#",g),u=r===-1?b===-1?void 0:b:b===-1?r:Math.min(r,b),U=$.slice(n,u);return oP(U.includes("%25")?U.replace(/%25/g,"%2525"):U)}else if(v===63||v===35)break}return $.slice(n,g)};var rk=(i)=>{let $=r_(i);return $.length>1&&$.at(-1)==="/"?$.slice(0,-1):$},ei=(i,$,...n)=>{if(n.length)$=ei($,...n);return`${i?.[0]==="/"?"":"/"}${i}${$==="/"?"":`${i?.at(-1)==="/"?"":"/"}${$?.[0]==="/"?$.slice(1):$}`}`},Ir=(i)=>{if(i.charCodeAt(i.length-1)!==63||!i.includes(":"))return null;let $=i.split("/"),n=[],g="";return $.forEach((v)=>{if(v!==""&&!/\:/.test(v))g+="/"+v;else if(/\:/.test(v))if(/\?/.test(v)){if(n.length===0&&g==="")n.push("/");else n.push(g);let r=v.replace("?","");g+="/"+r,n.push(g)}else g+="/"+v}),n.filter((v,r,b)=>b.indexOf(v)===r)},n_=(i)=>{if(!/[%+]/.test(i))return i;if(i.indexOf("+")!==-1)i=i.replace(/\+/g," ");return i.indexOf("%")!==-1?$v(i,gn):i},bk=(i,$,n)=>{let g;if(!n&&$&&!/[%+]/.test($)){let b=i.indexOf("?",8);if(b===-1)return;if(!i.startsWith($,b+1))b=i.indexOf(`&${$}`,b+1);while(b!==-1){let u=i.charCodeAt(b+$.length+1);if(u===61){let U=b+$.length+2,I=i.indexOf("&",U);return n_(i.slice(U,I===-1?void 0:I))}else if(u==38||isNaN(u))return"";b=i.indexOf(`&${$}`,b+1)}if(g=/[%+]/.test(i),!g)return}let v=Object.create(null);g??=/[%+]/.test(i);let r=i.indexOf("?",8);while(r!==-1){let b=i.indexOf("&",r+1),u=i.indexOf("=",r);if(u>b&&b!==-1)u=-1;let U=i.slice(r+1,u===-1?b===-1?void 0:b:u);if(g)U=n_(U);if(r=b,U==="")continue;let I;if(u===-1)I="";else if(I=i.slice(u+1,b===-1?void 0:b),g)I=n_(I);if(n){if(!(v[U]&&Array.isArray(v[U])))v[U]=[];v[U].push(I)}else v[U]??=I}return $?v[$]:v},uk=bk,Uk=(i,$)=>{return bk(i,$,!0)},gn=decodeURIComponent;var _k=/^[\w!#$%&'*.^`|~+-]+$/,mP=/^[ !#-:<-[\]-~]*$/,Ik=(i)=>{let $=0,n=i.length;while($<n){let g=i.charCodeAt($);if(g!==32&&g!==9)break;$++}while(n>$){let g=i.charCodeAt(n-1);if(g!==32&&g!==9)break;n--}return $===0&&n===i.length?i:i.slice($,n)},b_=(i,$)=>{if($&&i.indexOf($)===-1)return{};let n=i.split(";"),g=Object.create(null);for(let v of n){let r=v.indexOf("=");if(r===-1)continue;let b=Ik(v.substring(0,r));if($&&$!==b||!_k.test(b)||b in g)continue;let u=Ik(v.substring(r+1));if(u.startsWith('"')&&u.endsWith('"'))u=u.slice(1,-1);if(mP.test(u)){if(g[b]=u.indexOf("%")!==-1?$v(u,gn):u,$)break}}return g};var yP=(i,$,n={})=>{if(!_k.test(i))throw Error("Invalid cookie name");let g=`${i}=${$}`;if(i.startsWith("__Secure-")&&!n.secure)throw Error("__Secure- Cookie must have Secure attributes");if(i.startsWith("__Host-")){if(!n.secure)throw Error("__Host- Cookie must have Secure attributes");if(n.path!=="/")throw Error('__Host- Cookie must have Path attributes with "/"');if(n.domain)throw Error("__Host- Cookie must not have Domain attributes")}for(let v of["domain","path","sameSite","priority"])if(n[v]&&/[;\r\n]/.test(n[v]))throw Error(`${v} must not contain ";", "\\r", or "\\n"`);if(n&&typeof n.maxAge==="number"&&n.maxAge>=0){if(n.maxAge>34560000)throw Error("Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration.");g+=`; Max-Age=${n.maxAge|0}`}if(n.domain&&n.prefix!=="host")g+=`; Domain=${n.domain}`;if(n.path)g+=`; Path=${n.path}`;if(n.expires){if(n.expires.getTime()-Date.now()>34560000000)throw Error("Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future.");g+=`; Expires=${n.expires.toUTCString()}`}if(n.httpOnly)g+="; HttpOnly";if(n.secure)g+="; Secure";if(n.sameSite)g+=`; SameSite=${n.sameSite.charAt(0).toUpperCase()+n.sameSite.slice(1)}`;if(n.priority)g+=`; Priority=${n.priority.charAt(0).toUpperCase()+n.priority.slice(1)}`;if(n.partitioned){if(!n.secure)throw Error("Partitioned Cookie must have Secure attributes");g+="; Partitioned"}return g},u_=(i,$,n)=>{return $=encodeURIComponent($),yP(i,$,n)};var Dk=(i,$)=>{return i=i.replace(/\/+$/,""),i=i+"/",$=$.replace(/^\/+/,""),i+$},Dr=(i,$)=>{for(let[n,g]of Object.entries($)){let v=new RegExp("/:"+n+"(?:{[^/]+})?\\??(?=/|$)");i=i.replace(v,g?`/${g}`:"")}return i},kk=(i)=>{let $=new URLSearchParams;for(let[n,g]of Object.entries(i)){if(g===void 0)continue;if(Array.isArray(g))for(let v of g)$.append(n,v);else $.set(n,g)}return $},wk=(i,$)=>{switch($){case"ws":return i.replace(/^http/,"ws");case"http":return i.replace(/^ws/,"http")}},U_=(i)=>{if(/^https?:\/\/[^\/]+?\/index(?=\?|$)/.test(i))return i.replace(/\/index(?=\?|$)/,"/");return i.replace(/\/index(?=\?|$)/,"")};function _r(i){return typeof i==="object"&&i!==null&&!Array.isArray(i)}function I_(i,$){if(!_r(i)&&!_r($))return $;let n={...i};for(let g in $){let v=$[g];if(_r(n[g])&&_r(v))n[g]=I_(n[g],v);else n[g]=v}return n}var jk=(i,$)=>{return new Proxy(()=>{},{get(g,v){if(typeof v!=="string"||v==="then")return;return jk(i,[...$,v])},apply(g,v,r){return i({path:$,args:r})}})},dP=class{url;method;buildSearchParams;queryParams=void 0;pathParams={};rBody;cType=void 0;constructor(i,$,n){this.url=i,this.method=$,this.buildSearchParams=n.buildSearchParams}fetch=async(i,$)=>{if(i){if(i.query)this.queryParams=this.buildSearchParams(i.query);if(i.form){let u=new FormData;for(let[U,I]of Object.entries(i.form)){if(I===void 0)continue;if(Array.isArray(I))for(let _ of I)u.append(U,_);else u.append(U,I)}this.rBody=u}if(i.json)this.rBody=JSON.stringify(i.json),this.cType="application/json";if(i.param)this.pathParams=i.param}let n=this.method.toUpperCase(),g={...i?.header,...typeof $?.headers==="function"?await $.headers():$?.headers};if(i?.cookie){let u=[];for(let[U,I]of Object.entries(i.cookie))u.push(u_(U,I,{path:"/"}));g.Cookie=u.join(",")}if(this.cType)g["Content-Type"]=this.cType;let v=new Headers(g??void 0),r=this.url;if(r=U_(r),r=Dr(r,this.pathParams),this.queryParams)r=r+"?"+this.queryParams.toString();n=this.method.toUpperCase();let b=!(n==="GET"||n==="HEAD");return($?.fetch||fetch)(r,{body:b?this.rBody:void 0,method:n,headers:v,...$?.init})}},__=(i,$)=>jk(function n(g){let v=$?.buildSearchParams??kk,r=[...g.path],b=r.slice(-3).reverse();if(b[0]==="toString"){if(b[1]==="name")return b[2]||"";return n.toString()}if(b[0]==="valueOf"){if(b[1]==="name")return b[2]||"";return n}let u="";if(/^\$/.test(b[0])){let k=r.pop();if(k)u=k.replace(/^\$/,"")}let U=r.join("/"),I=Dk(i,U);if(u==="url"||u==="path"){let k=I;if(g.args[0]){if(g.args[0].param)k=Dr(I,g.args[0].param);if(g.args[0].query)k=k+"?"+v(g.args[0].query).toString()}if(k=U_(k),u==="url")return new URL(k);return k.slice(i.replace(/\/+$/,"").length).replace(/^\/?/,"/")}if(u==="ws"){let k=wk(g.args[0]&&g.args[0].param?Dr(I,g.args[0].param):I,"ws"),O=new URL(k),J=g.args[0]?.query;if(J)Object.entries(J).forEach(([A,z])=>{if(Array.isArray(z))z.forEach((B)=>O.searchParams.append(A,B));else O.searchParams.set(A,z)});return((...A)=>{if($?.webSocket!==void 0&&typeof $.webSocket==="function")return $.webSocket(...A);return new WebSocket(...A)})(O.toString())}let _=new dP(I,u,{buildSearchParams:v});if(u){$??={};let k={...g.args[1]},O=$.headers,J=k.headers;if(O&&J)k.headers=async()=>({...typeof O==="function"?await O():O,...typeof J==="function"?await J():J});let Q=I_($,k);return _.fetch(g.args[0],Q)}return _},[]);function xi(i){return`http://127.0.0.1:${i}`}import{join as Ok}from"path";class rn{millis(){return this.now().getTime()}iso(){return this.now().toISOString()}}class Ni extends rn{now(){return new Date}}var aP=new F,pP=new R,eP=new Di,sP=new Ni;class D_{descriptors;fs;process;http;clock;logger;diagnosticLog;signal;dir;constructor(i){this.descriptors=new Map(i.descriptors.map(($)=>[$.type,$])),this.fs=i.fs??aP,this.process=i.process??pP,this.http=i.http??eP,this.clock=i.clock??sP,this.logger=i.logger,this.diagnosticLog=i.diagnosticLog,this.signal=i.signal,this.dir=i.dir??ji,Object.freeze(this)}has(i){return this.descriptors.has(i)}types(){return[...this.descriptors.keys()]}createListener(i,$){return this.require($.type).createListener($,this.listenerDeps(i))}createAdapter(i){let $=this.require(i.type);if(!$.createAdapter)return null;return $.createAdapter(i,this.adapterDeps())}secretTokens(i){return this.require(i.type).secretTokens(i)}buildConfig(i,$){let n=typeof i.type==="string"?i.type:"";return this.require(n).buildConfig(i,$)}applyUpdate(i,$,n){return this.require(i.type).applyUpdate(i,$,n)}runOperation(i,$,n,g){let r=this.require(i.type).operations[$];if(!r)throw Error(`connector type "${i.type}" has no operation "${$}"`);return r({config:i,args:n,context:g})}connectorDir(i,$){return Ok(this.dir,"channels",i,"connectors",$)}channelDir(i){return Ok(this.dir,"channels",i)}require(i){let $=this.descriptors.get(i);if(!$)throw Error(`unknown connector type "${i}". Pass its descriptor to new Funnel({ connectors: [...] }).`);return $}listenerDeps(i){return{channelId:i,fs:this.fs,process:this.process,http:this.http,clock:this.clock,logger:this.logger,diagnosticLog:this.diagnosticLog,signal:this.signal,connectorDir:($,n)=>this.connectorDir($,n)}}adapterDeps(){return{fs:this.fs,process:this.process,http:this.http,logger:this.logger}}}var iW=new Ni,$W=new Zi;class k_{store;registry;profileChecker;clock;idGenerator;constructor(i){this.store=i.store,this.registry=i.registry,this.profileChecker=i.profileChecker??null,this.clock=i.clock??iW,this.idGenerator=i.idGenerator??$W,Object.freeze(this)}list(){return this.store.read().channels}get(i){return this.list().find(($)=>$.name===i)??null}getById(i){return this.list().find(($)=>$.id===i)??null}add(i){return this.store.update(($)=>{if($.channels.some((g)=>g.name===i.name))throw new rr(i.name);let n={id:this.idGenerator.generate(),name:i.name,delivery:i.delivery??"fanout",connectors:[]};return $.channels.push(n),n})}setDelivery(i,$){this.store.update((n)=>{let g=this.requireChannel(n,i);g.delivery=$})}remove(i){this.store.update(($)=>{let n=$.channels.findIndex((v)=>v.name===i);if(n<0)throw new sv(i);let g=$.channels[n];if(g&&this.profileChecker?.hasChannelRef(g.id))throw Error(`channel "${i}" is referenced by a profile`);$.channels.splice(n,1)})}rename(i,$){this.store.update((n)=>{let g=n.channels.find((v)=>v.name===i);if(!g)throw new sv(i);if(n.channels.some((v)=>v.name===$))throw new rr($);g.name=$})}listConnectors(i){return this.requireChannel(this.store.read(),i).connectors}getConnector(i,$){let n=this.get(i);if(!n)return null;return n.connectors.find((g)=>g.name===$)??null}listAllConnectors(){let i=[];for(let $ of this.list())for(let n of $.connectors)i.push({...n,channelId:$.id,channelName:$.name});return i}addConnector(i,$){return this.store.update((n)=>{let g=this.requireChannel(n,i);if(g.connectors.some((r)=>r.name===$.name))throw Error(`connector "${$.name}" already exists in channel "${i}"`);let v=this.registry.buildConfig($,{id:this.idGenerator.generate(),now:this.clock.iso()});return this.assertNoTokenCollision(n,v),g.connectors.push(v),v})}removeConnector(i,$){this.store.update((n)=>{let g=this.requireChannel(n,i),v=g.connectors.findIndex((r)=>r.name===$);if(v<0)throw new iv(i,$);g.connectors.splice(v,1)})}renameConnector(i,$,n){this.store.update((g)=>{let v=this.requireChannel(g,i),r=v.connectors.find((b)=>b.name===$);if(!r)throw Error(`connector "${$}" not found in channel "${i}"`);if(v.connectors.some((b)=>b.name===n))throw Error(`connector "${n}" already exists in channel "${i}"`);r.name=n,r.updatedAt=this.clock.iso()})}updateConnector(i,$,n){this.store.update((g)=>{let v=this.requireChannel(g,i),r=v.connectors.find((u)=>u.name===$);if(!r)throw new iv(i,$);let b=this.registry.applyUpdate(r,n,{now:this.clock.iso()});this.assertNoTokenCollision(g,b),this.replaceConnector(v,r.name,b)})}updateSlackConnector(i,$,n){this.updateConnector(i,$,n)}updateGhConnector(i,$,n){this.updateConnector(i,$,n)}updateDiscordConnector(i,$,n){this.updateConnector(i,$,n)}connectorOp(i,$,n,g){return this.store.update((v)=>{let r=this.requireChannel(v,i),b=r.connectors.find((U)=>U.name===$);if(!b)throw new iv(i,$);let u=this.registry.runOperation(b,n,g,{generateId:()=>this.idGenerator.generate(),now:this.clock.iso()});if(u.config!==b)this.replaceConnector(r,b.name,u.config);return u.result})}async call(i,$,n){let g=this.getConnector(i,$);if(!g)throw new iv(i,$);let v=this.registry.createAdapter(g);if(!v)throw Error(`connector type "${g.type}" does not support outbound calls`);return await v.call(n)}createListener(i,$){let n=this.get(i);if(!n)return null;let g=n.connectors.find((v)=>v.name===$);if(!g)return null;return{config:g,channelId:n.id,listener:this.registry.createListener(n.id,g)}}createAllListeners(){let i=[];for(let $ of this.list())for(let n of $.connectors)i.push({config:n,channelId:$.id,channelName:$.name,listener:this.registry.createListener($.id,n)});return i}requireChannel(i,$){let n=i.channels.find((g)=>g.name===$);if(!n)throw new sv($);return n}replaceConnector(i,$,n){let g=i.connectors.findIndex((v)=>v.name===$);if(g<0)throw Error(`connector "${$}" not found in channel "${i.name}"`);i.connectors[g]=n}assertNoTokenCollision(i,$){let n=this.registry.secretTokens($);if(n.length===0)return;for(let g of i.channels)for(let v of g.connectors){if(v.id===$.id)continue;for(let r of this.registry.secretTokens(v))if(n.includes(r))throw Error(`token already in use by connector "${v.name}" in channel "${g.name}"`)}}}var vW=new R,nW=new Zi;class w_{channels;mcp;gateway;sessions;guard;process;idGenerator;logger;dir;constructor(i){this.channels=i.channels,this.mcp=i.mcp,this.gateway=i.gateway,this.sessions=i.sessions,this.guard=i.guard,this.process=i.process??vW,this.idGenerator=i.idGenerator??nW,this.logger=i.logger,this.dir=i.dir,Object.freeze(this)}async launch(i){let $=this.channels.get(i.channel)??this.channels.getById(i.channel);if(!$)throw Error(`channel "${i.channel}" not found`);if(i.profileId&&this.guard.isRunning(i.profileId))throw Error(`profile "${i.profileId}" is already running`);let n=i.cwd??globalThis.process.cwd();if((i.installMcp??!0)&&!this.mcp.findInstalledName(n))this.mcp.install(n),this.logger?.info("added funnel MCP to .mcp.json",{cwd:n});if(!this.gateway.isRunning()){if(this.logger?.info("starting gateway automatically"),!await this.gateway.start())throw Error(`gateway failed to start on port ${Mi()}; another funnel daemon (a different repo/scope) may already hold it. See 'fnl gateway logs' and 'ps -o pid,args= | grep funnel-gateway'.`)}if(i.profileId)this.guard.acquire(i.profileId);let r=(i.resume??!1)&&i.profileId?this.resolveSession(i.profileId,n,i.userArgs??[],i.env??{}):null,b=this.buildArgs(i.options??[],i.userArgs??[],n,r),u=this.buildEnv($.id,i.env??{});this.logger?.info("claude launch",{channel:i.channel,channelId:$.id,cwd:n});try{return await this.process.attach(["claude",...b],{cwd:n,env:u,onSpawned:i.onSpawned})}finally{if(i.profileId)this.guard.release(i.profileId)}}buildArgs(i,$,n,g){let v=[...i,...$];if(g!==null)if(g.mode==="resume")v.push("--resume",g.id);else v.push("--session-id",g.id);let r=this.mcp.findInstalledName(n);if(r&&!v.includes("--dangerously-load-development-channels")&&!v.includes("--channels"))v.push("--dangerously-load-development-channels",`server:${r}`);return v}resolveSession(i,$,n,g){for(let b of n){if(b==="-c"||b==="--continue")return null;if(b==="--resume"||b.startsWith("--resume="))return null;if(b==="--session-id"||b.startsWith("--session-id="))return null}let v=this.sessions.getSessionId(i);if(v!==null&&this.sessions.sessionFileExists($,v,g))return{id:v,mode:"resume"};let r=this.idGenerator.generate();return this.sessions.setSessionId(i,r),{id:r,mode:"new"}}buildEnv(i,$){let n={};for(let[g,v]of Object.entries($))n[g]=v;for(let[g,v]of Object.entries(globalThis.process.env))if(typeof v==="string")n[g]=v;if(n.FUNNEL_CHANNEL_ID=i,n.FUNNEL_PORT=String(Mi()),this.dir!==void 0)n.FUNNEL_DIR=this.dir;return n}}import{join as Jk}from"path";var gW=new F,rW=new R;class kr{fs;process;pidDir;constructor(i={}){this.fs=i.fs??gW,this.process=i.process??rW,this.pidDir=Jk(i.dir??ji,"claude"),Object.freeze(this)}isRunning(i){let $=this.readRecord(i);if(!$)return!1;if(!this.process.isAlive($.pid))return this.release(i),!1;if($.startTime!==null){let n=this.process.getStartTime($.pid);if(n===null)return this.release(i),!1;if(n!==$.startTime)return this.release(i),!1}return!0}acquire(i){this.fs.mkdirSync(this.pidDir,{recursive:!0});let $=globalThis.process.pid,n=this.process.getStartTime($),g={pid:$,startTime:n};this.fs.writeFileSync(this.pidPath(i),JSON.stringify(g)),globalThis.process.once("exit",()=>this.release(i))}release(i){let $=this.pidPath(i);if(this.fs.existsSync($))this.fs.unlink($)}pidPath(i){return Jk(this.pidDir,`${i}.pid`)}readRecord(i){let $=this.pidPath(i);if(!this.fs.existsSync($))return null;try{let n=this.fs.readFileSync($).trim();if(!n)return null;if(n.startsWith("{")){let v=JSON.parse(n),r=typeof v.pid==="number"?v.pid:Number(v.pid);if(!Number.isInteger(r)||r<=0)return null;let b=typeof v.startTime==="string"?v.startTime:null;return{pid:r,startTime:b}}let g=Number(n);if(!g||g<=0)return null;return{pid:g,startTime:null}}catch{return null}}}import{dirname as Wk,join as uW}from"path";import{homedir as bW}from"os";import{join as Pk}from"path";function wr(i){let $=i.env.CLAUDE_CONFIG_DIR??globalThis.process.env.CLAUDE_CONFIG_DIR??Pk(bW(),".claude"),n=i.cwd.replace(/\//g,"-"),g=Pk($,"projects",n,`${i.sessionId}.jsonl`);if(!i.fs.existsSync(g))return!1;return i.fs.readFileSync(g).trim().length>0}var UW=D.record(D.string(),D.string());class j_{fs;path;constructor(i){this.fs=i.fs,this.path=uW(i.dir,"claude","local-sessions.json"),Object.freeze(this)}getSessionId(i){return this.fs.mkdirSync(Wk(this.path),{recursive:!0}),this.fs.withFileLock(`${this.path}.lock`,()=>this.read()[i]??null)}setSessionId(i,$){this.fs.mkdirSync(Wk(this.path),{recursive:!0}),this.fs.withFileLock(`${this.path}.lock`,()=>{let n=this.read();n[i]=$,this.fs.writeFileSync(this.path,`${JSON.stringify(n,null,2)}
75
- `)})}sessionFileExists(i,$,n){return wr({fs:this.fs,cwd:i,sessionId:$,env:n})}read(){if(!this.fs.existsSync(this.path))return{};let i=JSON.parse(this.fs.readFileSync(this.path)),$=UW.safeParse(i);if(!$.success)throw Error(`invalid local profile session store (${this.path})`);return $.data}}class O_{global;local;constructor(i){this.global=i.global,this.local=i.local,Object.freeze(this)}getSessionId(i){return this.storeFor(i).getSessionId(i)}setSessionId(i,$){this.storeFor(i).setSessionId(i,$)}sessionFileExists(i,$,n){return this.global.sessionFileExists(i,$,n)}storeFor(i){return i.startsWith("local-")?this.local:this.global}}import{existsSync as _W,readFileSync as DW}from"fs";import{join as kW}from"path";var J$=async(i,$={},n=5000)=>{let g=AbortSignal.timeout(n),v=$.signal?AbortSignal.any([$.signal,g]):g;return fetch(i,{...$,signal:v})};var bn=(i)=>typeof i==="string"&&i.length>0?i:null;var IW=(i)=>i!==null&&typeof i==="object"&&!Array.isArray(i),J_=(i)=>{if(i===null)return null;try{let $=JSON.parse(i);if(IW($))return $}catch{return null}return null},Vk=(i,$)=>i.length<=$?i:`${i.slice(0,$)}\u2026`,Xk=(i)=>{if(typeof i!=="string"||i.length===0)return null;let $=J_(i);if($!==null&&"text"in $)return Vk(String($.text),60);return Vk(i,60)};var vv=(i)=>({seq:i.seq,ts:i.ts,type:i.type,outcome:i.outcome,eventId:i.eventId,payload:bn(i.payload),payloadParsed:J_(bn(i.payload)),preview:Xk(i.payload)}),fk=(i)=>({seq:i.seq,ts:i.ts,type:i.type,outcome:"",eventId:i.eventId,payload:bn(i.payload),payloadParsed:J_(bn(i.payload)),preview:Xk(i.payload)}),P$=(i)=>({seq:i.seq,ts:i.ts,type:i.type,status:i.status,detail:bn(i.detail)});var wW=(i)=>{if(i===null||typeof i!=="object")return!1;if(!("clients"in i)||!Array.isArray(i.clients))return!1;if(!("listeners"in i)||!Array.isArray(i.listeners))return!1;return!0},jW=(i,$)=>{if($===null)return;return i.connectors?.find((n)=>n.id===$)?.name},Qk=3,OW=100,Kk=(i)=>`${i.type}\x00${i.connectorId??""}`,JW=(i)=>{let $=new Map;return i.forEach((n,g)=>{if(n.status==="connected")$.set(Kk(n),g)}),i.filter((n,g)=>{if(n.status!=="auth-failed"&&n.status!=="error")return!1;return($.get(Kk(n))??-1)<g})},PW=(i,$=i.connectionErrors)=>{let g=($[$.length-1]??null)?.detail??null,v=i.channel;if(!i.gateway.running)return{status:"error",message:"gateway is not running",nextActions:["fnl gateway start"],rootCause:null};if(i.gateway.statusError!==null)return{status:"error",message:`gateway running but status probe failed: ${i.gateway.statusError}`,nextActions:["fnl gateway restart"],rootCause:i.gateway.statusError};if(i.configuredConnectors>i.listeners.length)return{status:"error",message:`${i.configuredConnectors} connector(s) configured but ${i.listeners.length} registered with supervisor`,nextActions:["fnl gateway restart"],rootCause:"supervisor missing listeners declared in settings.json"};if(i.configuredConnectors===0)return{status:"warn",message:"no connectors configured on this channel",nextActions:[`fnl channels ${v} connectors add <name> --type=slack ...`],rootCause:null};let r=$.filter((k)=>k.status==="auth-failed");if(r.length>0){let k=r[r.length-1]?.detail??null;return{status:"error",message:"connector credentials rejected (auth-failed)",nextActions:[`fnl channels ${v} connectors set <connector> --bot-token=<new>`,"fnl gateway restart"],rootCause:k??"token rejected by upstream auth.test"}}let b=i.listeners.every((k)=>!k.alive),u=i.listeners.some((k)=>!k.alive);if(b)return{status:"error",message:"all listeners are dead",nextActions:["fnl doctor --fix","fnl doctor --fix --aggressive"],rootCause:g};if(u)return{status:"warn",message:"some listeners are dead",nextActions:["fnl doctor --fix"],rootCause:g};let U=i.listeners.filter((k)=>k.errors>=Qk);if(U.length>0)return{status:"warn",message:`listener(s) flapping (\u2265${Qk} errors): ${U.map((k)=>k.name).join(", ")}`,nextActions:["fnl gateway logs"],rootCause:g};if(i.claudeClients===0)return{status:"warn",message:"no Claude connected to this channel",nextActions:[`fnl claude --channel ${v}`],rootCause:null};if(i.listeners.some((k)=>k.errors>0))return{status:"warn",message:"listeners have errors",nextActions:["fnl gateway logs"],rootCause:g};let _=WW(i);if(_!==null)return _;return{status:"ok",message:"everything looks healthy",nextActions:[],rootCause:null}},WW=(i)=>{if(!i.listeners.some((r)=>r.type==="slack")||i.recentEvents.length===0)return null;let n=i.recentEvents.filter((r)=>r.type==="slack").map((r)=>r.payloadParsed?.type);if(n.length===0)return null;let g=n.includes("app_mention"),v=n.includes("message");if(!g||v)return null;return{status:"warn",message:"Slack is only delivering app_mention events; unmentioned thread replies may not arrive",nextActions:["Add Slack bot events: message.channels, message.groups, message.im, message.mpim; reinstall the app; then restart the gateway"],rootCause:"Slack Event Subscriptions likely omit message.* events"}};class P_{props;constructor(i){this.props=i;Object.freeze(this)}async diagnose(i){let $=this.props.channels.list(),n=i?$.find((v)=>v.name===i)??null:$[0]??null;if(!n)return null;let g=await this.fetchGatewayStatus();return this.buildChannelDiagnosis(n,g,5)}async diagnoseAll(){let i=this.props.channels.list(),$=await this.fetchGatewayStatus(),n=await Promise.all(i.map((u)=>this.buildChannelDiagnosis(u,$,5))),g=n.filter((u)=>u.diagnosis.status==="error").map((u)=>u.channel),v=n.filter((u)=>u.diagnosis.status==="warn").map((u)=>u.channel),r=n.filter((u)=>u.diagnosis.status==="ok").map((u)=>u.channel),b=[...new Set(n.flatMap((u)=>u.diagnosis.nextActions))];return{summary:{total:n.length,ok:r.length,warn:v.length,error:g.length,criticalChannels:g,warnChannels:v,suggestedActions:b},channels:n}}async recentEvents(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryProcessed({...g,limit:n})).map(vv)}async droppedEvents(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryProcessed({...g,outcomePrefix:"skip:",limit:n})).map(vv)}async rawEvents(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryRaw({...g,limit:n})).map(fk)}async connectionErrors(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryConnection({...g,statuses:["auth-failed","error"],limit:n})).map(P$)}async connectionTimeline(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryConnection({...g,limit:n})).map(P$)}async recentLogs(i={}){let $=i.limit??200,n=kW(this.props.tmpDir,"funnel.log");if(!_W(n))return{lines:[],path:null,truncated:!1};let g;try{g=DW(n,"utf-8")}catch(I){return{lines:[`(read failed: ${E(I)})`],path:n,truncated:!1}}let v=g.split(`
75
+ `)})}sessionFileExists(i,$,n){return wr({fs:this.fs,cwd:i,sessionId:$,env:n})}read(){if(!this.fs.existsSync(this.path))return{};let i=JSON.parse(this.fs.readFileSync(this.path)),$=UW.safeParse(i);if(!$.success)throw Error(`invalid local profile session store (${this.path})`);return $.data}}class O_{global;local;constructor(i){this.global=i.global,this.local=i.local,Object.freeze(this)}getSessionId(i){return this.storeFor(i).getSessionId(i)}setSessionId(i,$){this.storeFor(i).setSessionId(i,$)}sessionFileExists(i,$,n){return this.global.sessionFileExists(i,$,n)}storeFor(i){return i.startsWith("local-")?this.local:this.global}}import{existsSync as _W,readFileSync as DW}from"fs";import{join as kW}from"path";var J$=async(i,$={},n=5000)=>{let g=AbortSignal.timeout(n),v=$.signal?AbortSignal.any([$.signal,g]):g;return fetch(i,{...$,signal:v})};var bn=(i)=>typeof i==="string"&&i.length>0?i:null;var IW=(i)=>i!==null&&typeof i==="object"&&!Array.isArray(i),J_=(i)=>{if(i===null)return null;try{let $=JSON.parse(i);if(IW($))return $}catch{return null}return null},Vk=(i,$)=>i.length<=$?i:`${i.slice(0,$)}\u2026`,Xk=(i)=>{if(typeof i!=="string"||i.length===0)return null;let $=J_(i);if($!==null&&"text"in $)return Vk(String($.text),60);return Vk(i,60)};var vv=(i)=>({seq:i.seq,ts:i.ts,type:i.type,outcome:i.outcome,eventId:i.eventId,payload:bn(i.payload),payloadParsed:J_(bn(i.payload)),preview:Xk(i.payload)}),fk=(i)=>({seq:i.seq,ts:i.ts,type:i.type,outcome:"",eventId:i.eventId,payload:bn(i.payload),payloadParsed:J_(bn(i.payload)),preview:Xk(i.payload)}),P$=(i)=>({seq:i.seq,ts:i.ts,type:i.type,status:i.status,detail:bn(i.detail)});var wW=(i)=>{if(i===null||typeof i!=="object")return!1;if(!("clients"in i)||!Array.isArray(i.clients))return!1;if(!("listeners"in i)||!Array.isArray(i.listeners))return!1;return!0},jW=(i,$)=>{if($===null)return;return i.connectors?.find((n)=>n.id===$)?.name},Qk=3,OW=100,Kk=(i)=>`${i.type}\x00${i.connectorId??""}`,JW=(i)=>{let $=new Map;return i.forEach((n,g)=>{if(n.status==="connected")$.set(Kk(n),g)}),i.filter((n,g)=>{if(n.status!=="auth-failed"&&n.status!=="error")return!1;return($.get(Kk(n))??-1)<g})},PW=(i,$=i.connectionErrors)=>{let g=($[$.length-1]??null)?.detail??null,v=i.channel;if(!i.gateway.running)return{status:"error",message:"gateway is not running",nextActions:["fnl gateway start"],rootCause:null};if(i.gateway.statusError!==null)return{status:"error",message:`gateway running but status probe failed: ${i.gateway.statusError}`,nextActions:["fnl gateway restart"],rootCause:i.gateway.statusError};if(i.configuredConnectors>i.listeners.length)return{status:"error",message:`${i.configuredConnectors} connector(s) configured but ${i.listeners.length} registered with supervisor`,nextActions:["fnl gateway restart"],rootCause:"supervisor missing listeners declared in settings.json"};let r=$.filter((k)=>k.status==="auth-failed");if(r.length>0){let k=r[r.length-1]?.detail??null;return{status:"error",message:"connector credentials rejected (auth-failed)",nextActions:[`fnl channels ${v} connectors set <connector> --bot-token=<new>`,"fnl gateway restart"],rootCause:k??"token rejected by upstream auth.test"}}let b=i.listeners.length>0&&i.listeners.every((k)=>!k.alive),u=i.listeners.some((k)=>!k.alive);if(b)return{status:"error",message:"all listeners are dead",nextActions:["fnl doctor --fix","fnl doctor --fix --aggressive"],rootCause:g};if(u)return{status:"warn",message:"some listeners are dead",nextActions:["fnl doctor --fix"],rootCause:g};let U=i.listeners.filter((k)=>k.errors>=Qk);if(U.length>0)return{status:"warn",message:`listener(s) flapping (\u2265${Qk} errors): ${U.map((k)=>k.name).join(", ")}`,nextActions:["fnl gateway logs"],rootCause:g};if(i.claudeClients===0)return{status:"warn",message:"no Claude connected to this channel",nextActions:[`fnl claude --channel ${v}`],rootCause:null};if(i.listeners.some((k)=>k.errors>0))return{status:"warn",message:"listeners have errors",nextActions:["fnl gateway logs"],rootCause:g};let _=WW(i);if(_!==null)return _;return{status:"ok",message:"everything looks healthy",nextActions:[],rootCause:null}},WW=(i)=>{if(!i.listeners.some((r)=>r.type==="slack")||i.recentEvents.length===0)return null;let n=i.recentEvents.filter((r)=>r.type==="slack").map((r)=>r.payloadParsed?.type);if(n.length===0)return null;let g=n.includes("app_mention"),v=n.includes("message");if(!g||v)return null;return{status:"warn",message:"Slack is only delivering app_mention events; unmentioned thread replies may not arrive",nextActions:["Add Slack bot events: message.channels, message.groups, message.im, message.mpim; reinstall the app; then restart the gateway"],rootCause:"Slack Event Subscriptions likely omit message.* events"}};class P_{props;constructor(i){this.props=i;Object.freeze(this)}async diagnose(i){let $=this.props.channels.list(),n=i?$.find((v)=>v.name===i)??null:$[0]??null;if(!n)return null;let g=await this.fetchGatewayStatus();return this.buildChannelDiagnosis(n,g,5)}async diagnoseAll(){let i=this.props.channels.list(),$=await this.fetchGatewayStatus(),n=await Promise.all(i.map((u)=>this.buildChannelDiagnosis(u,$,5))),g=n.filter((u)=>u.diagnosis.status==="error").map((u)=>u.channel),v=n.filter((u)=>u.diagnosis.status==="warn").map((u)=>u.channel),r=n.filter((u)=>u.diagnosis.status==="ok").map((u)=>u.channel),b=[...new Set(n.flatMap((u)=>u.diagnosis.nextActions))];return{summary:{total:n.length,ok:r.length,warn:v.length,error:g.length,criticalChannels:g,warnChannels:v,suggestedActions:b},channels:n}}async recentEvents(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryProcessed({...g,limit:n})).map(vv)}async droppedEvents(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryProcessed({...g,outcomePrefix:"skip:",limit:n})).map(vv)}async rawEvents(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryRaw({...g,limit:n})).map(fk)}async connectionErrors(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryConnection({...g,statuses:["auth-failed","error"],limit:n})).map(P$)}async connectionTimeline(i,$={}){let n=$.limit??20,g=this.resolveScope(i,$.connector);if(g===null)return[];return this.queryLog((v)=>v.queryConnection({...g,limit:n})).map(P$)}async recentLogs(i={}){let $=i.limit??200,n=kW(this.props.tmpDir,"funnel.log");if(!_W(n))return{lines:[],path:null,truncated:!1};let g;try{g=DW(n,"utf-8")}catch(I){return{lines:[`(read failed: ${E(I)})`],path:n,truncated:!1}}let v=g.split(`
76
76
  `).filter((I)=>I.length>0),r=i.grep?.toLowerCase(),b=r?v.filter((I)=>I.toLowerCase().includes(r)):v,u=b.length>$;return{lines:u?b.slice(b.length-$):b,path:n,truncated:u}}async replay(i,$){let g=this.props.channels.list().find((Q)=>Q.name===i);if(!g)return{state:"not-found"};if(!this.props.diagnosticLog)return{state:"error",reason:"no diagnostic log configured"};let v=this.queryLogResult((Q)=>Q.queryProcessed({channelId:g.id,...$!==void 0?{seq:$}:{outcomePrefix:"emitted"},limit:1}));if(v instanceof Error)return{state:"error",reason:v.message};let r=v[0];if(!r)return{state:"not-found"};let{seq:b,eventId:u,connectorId:U}=r,I=r.payload.length>0?r.payload:null,_=I===null?this.queryLog((Q)=>Q.queryRaw({eventId:u,limit:1}))[0]?.payload??null:null,k=I??_;if(!k)return{state:"error",reason:"event has no payload to replay"};let O=jW(g,U),J=await this.props.publisher.publish(g.name,{content:k,connector:O});if(J.state==="offline")return{state:"offline"};if(J.state==="error")return{state:"error",reason:J.reason};return{state:"ok",seq:b,offset:J.offset,preview:k.slice(0,60)}}resolveScope(i,$){if(!i)return{};let g=this.props.channels.list().find((r)=>r.name===i)??null;if(!g)return null;if(!$)return{channelId:g.id};let v=g.connectors?.find((r)=>r.name===$)?.id??null;if(!v)return null;return{channelId:g.id,connectorId:v}}queryLog(i){let $=this.queryLogResult(i);return $ instanceof Error?[]:$}queryLogResult(i){let $=this.props.diagnosticLog;if(!$)return Error("no diagnostic log configured");try{return i($)}catch(n){return Error(E(n))}}async fetchGatewayStatus(){let i=this.props.gateway.getStatus();if(!i.running)return{body:null,error:null};let $=this.props.gatewayToken.read(),n=$?{Authorization:`Bearer ${$}`}:{},g=null;try{g=await J$(`${xi(i.port)}/status`,{headers:n})}catch(r){return{body:null,error:`fetch failed: ${E(r)}`}}if(!g.ok)return{body:null,error:`gateway /status returned ${g.status}`};let v;try{v=await g.json()}catch(r){return{body:null,error:`gateway /status body parse failed: ${E(r)}`}}if(!wW(v))return{body:null,error:"gateway /status returned an unrecognized shape"};return{body:v,error:null}}async buildChannelDiagnosis(i,$,n){let g=this.props.gateway.getStatus(),v=i.name,r=$.body,b={channel:v,channelId:i.id,gateway:{running:g.running,pid:g.pid,port:g.running?g.port:null,uptimeMs:r?.uptimeMs??null,statusError:$.error},configuredConnectors:i.connectors?.length??0,listeners:[],claudeClients:0,recentEvents:[],connectionErrors:[]};if(r)b.listeners=r.listeners.filter((U)=>U.channelName===v).map((U)=>({name:U.name,type:U.type,alive:U.alive,events:U.events,errors:U.errors,lastEventAt:U.lastEventAt})),b.claudeClients=r.clients.filter((U)=>U.channelName===v).length;let u=[];if(this.props.diagnosticLog){let U=this.queryLog((k)=>k.queryProcessed({channelId:i.id,limit:n}));b.recentEvents=U.map(vv);let I=this.queryLog((k)=>k.queryConnection({channelId:i.id,limit:OW})),_=I.filter((k)=>k.status==="auth-failed"||k.status==="error").slice(-3);b.connectionErrors=_.map(P$),u=JW(I).map(P$)}return{...b,diagnosis:PW(b,u)}}}class W_{props;constructor(i){this.props=i;Object.freeze(this)}async run(i="off"){if(i==="off"){let u=await this.props.diagnostics.diagnoseAll();return this.buildReport({before:null,after:u,appliedActions:[],fixFailed:!1})}let $=await this.props.diagnostics.diagnoseAll(),n=[],g=!1;if($.channels.some((u)=>u.diagnosis.status==="error"&&!u.gateway.running)){let u=await this.props.recovery.ensureGatewayRunning();if(n.push(...u.actions),!u.ok)g=!0}if($.channels.some((u)=>u.listeners.some((U)=>!U.alive))){let u=await this.props.recovery.restartAllDeadListeners();if(n.push(...u.actions),!u.ok&&u.actions.length===0)g=!0}if(i==="aggressive"){if((await this.props.diagnostics.diagnoseAll()).channels.some((I)=>I.diagnosis.status==="error")){let I=await this.props.recovery.restartGateway();if(n.push(...I.actions),!I.ok)g=!0}}let b=await this.props.diagnostics.diagnoseAll();return this.buildReport({before:$,after:b,appliedActions:n,fixFailed:g})}buildReport(i){let $=i.after.channels.filter((v)=>v.diagnosis.status!=="ok").map((v)=>({channel:v.channel,diagnosis:v.diagnosis})),n=i.fixFailed?"error":$.length===0?"ok":"warn",g=VW({mode:i.before===null?"off":"fix",status:n,appliedCount:i.appliedActions.length,remainingCount:$.length});return{status:n,message:g,before:i.before,after:i.after,appliedActions:i.appliedActions,remainingIssues:$}}}var VW=(i)=>{if(i.mode==="off"){if(i.remainingCount===0)return"all channels healthy";return`${i.remainingCount} channel(s) need attention \u2014 run \`fnl doctor --fix\` to apply suggested fixes`}if(i.status==="error")return"one or more fix steps failed \u2014 check fnl gateway logs";if(i.status==="ok")return`applied ${i.appliedCount} fix(es), all channels healthy`;return`applied ${i.appliedCount} fix(es), ${i.remainingCount} channel(s) still need attention`};var Gk=`funnel docs architecture \u2014 how Funnel routes events
77
77
 
78
78
  Funnel is a hub between external sources (Slack / GitHub / Discord / cron) and
@@ -792,6 +792,6 @@ related: fnl docs architecture, fnl docs debugging, fnl docs mcp`;var qk=`funnel
792
792
 
793
793
  related: fnl docs debugging, fnl docs channels, fnl docs profiles`;var V_={architecture:Gk,channels:Yk,claude:Hk,connectors:zk,debugging:Sk,gateway:Ak,glossary:Bk,"local-config":lk,mcp:Lk,profiles:Tk,"programmable-api":Zk,recipes:qk},XW={architecture:"how Funnel routes events end-to-end",channels:"what a channel is and how delivery modes work",connectors:"external service bindings nested in channels",profiles:"named Claude launch presets",claude:"fnl claude resolution order and argv assembly",mcp:"the MCP server, inbound notifications, outbound tools",gateway:"the WebSocket + HTTP daemon","local-config":"the per-repo funnel.json file",debugging:"ladder for diagnosing event delivery problems","programmable-api":"Funnel as an SDK; build your own CLI/UI on the engine",recipes:"common task playbooks",glossary:"vocabulary reference"};class X_{constructor(){Object.freeze(this)}list(){return Object.keys(V_).sort().map((i)=>({name:i,summary:XW[i]??""}))}get(i){return V_[i]??null}topics(){return Object.keys(V_).sort()}}import{join as SW}from"path";var fW=D.object({type:D.literal("slack"),name:D.string(),minify:D.boolean().optional()}),QW=D.object({type:D.literal("discord"),name:D.string()}),KW=D.object({type:D.literal("gh"),name:D.string(),pollInterval:D.number().int().positive().optional()}),GW=D.object({type:D.literal("schedule"),name:D.string()}),YW=D.discriminatedUnion("type",[fW,QW,KW,GW]),HW=D.object({name:D.string(),connectors:D.array(YW).optional()}),zW=D.object({name:D.string(),channel:D.string(),options:D.array(D.string()).optional(),env:D.record(D.string(),D.string()).optional(),resume:D.boolean().optional()}),Mk=D.object({$schema:D.string().optional(),id:D.string().optional(),channels:D.array(HW).min(1),profiles:D.array(zW).optional()}),nv="funnel.json";class f_{fs;constructor(i){this.fs=i.fs,Object.freeze(this)}read(i){let $=SW(i,nv);if(!this.fs.existsSync($))return null;let n=this.fs.readFileSync($),g=(()=>{try{return JSON.parse(n)}catch(r){let b=r instanceof Error?r.message:String(r);throw Error(`${nv} is not valid JSON: ${b}`)}})(),v=Mk.safeParse(g);if(!v.success)throw Error(`${nv} is invalid: ${v.error.message}`);return this.assertProfilesValid(v.data),v.data}assertProfilesValid(i){let $=i.profiles??[];if($.length===0)return;let n=new Set(i.channels.map((v)=>v.name)),g=new Set;for(let v of $){if(!n.has(v.channel))throw Error(`${nv} is invalid: profile "${v.name}" binds channel "${v.channel}", which is not declared in channels[]`);if(g.has(v.name))throw Error(`${nv} is invalid: more than one profile is named "${v.name}" \u2014 names must be unique`);g.add(v.name)}}}function Q_(i){if(i.env!==void 0)return{botTokenEnv:i.env};if(i.literal!==void 0)return{botToken:i.literal};return{}}function xk(i){if(i.env!==void 0)return{appTokenEnv:i.env};if(i.literal!==void 0)return{appToken:i.literal};return{}}class K_{channels;prompter;constructor(i){this.channels=i.channels,this.prompter=i.prompter,Object.freeze(this)}async ensure(i){if(!this.channels.get(i.name))this.channels.add({name:i.name});if(i.connectors===void 0)return{touched:[],removed:[]};let n=[],g=new Set;for(let r of i.connectors){let b=await this.ensureConnector(i.name,r);n.push({name:b.name,changed:b.changed}),g.add(b.id)}let v=this.removeExtras(i.name,g);return{touched:n,removed:v}}async ensureConnector(i,$){if($.type==="slack")return await this.ensureSlack(i,$);if($.type==="discord")return await this.ensureDiscord(i,$);if($.type==="gh")return this.ensureGh(i,$);return this.ensureSchedule(i,$)}async ensureSlack(i,$){let n=this.findExistingSlack(i,$.name),g=await this.resolveSlot({label:`${$.name}.botToken`,existingLiteral:n?.botToken,existingEnv:n?.botTokenEnv}),v=await this.resolveSlot({label:`${$.name}.appToken`,existingLiteral:n?.appToken,existingEnv:n?.appTokenEnv}),r={botToken:g.token,botTokenEnv:g.tokenEnv,appToken:v.token,appTokenEnv:v.tokenEnv};if(n){if(!(n.botToken===g.token&&n.botTokenEnv===g.tokenEnv&&n.appToken===v.token&&n.appTokenEnv===v.tokenEnv))return this.channels.updateSlackConnector(i,$.name,r),{id:n.id,name:$.name,changed:!0};return{id:n.id,name:$.name,changed:!1}}return{id:this.channels.addConnector(i,{type:"slack",name:$.name,...Q_({literal:g.token,env:g.tokenEnv}),...xk({literal:v.token,env:v.tokenEnv}),...$.minify!==void 0?{minify:$.minify}:{}}).id,name:$.name,changed:!0}}async ensureDiscord(i,$){let n=this.findExistingDiscord(i,$.name),g=await this.resolveSlot({label:`${$.name}.botToken`,existingLiteral:n?.botToken,existingEnv:n?.botTokenEnv}),v={botToken:g.token,botTokenEnv:g.tokenEnv};if(n){if(n.botToken!==g.token||n.botTokenEnv!==g.tokenEnv)return this.channels.updateDiscordConnector(i,$.name,v),{id:n.id,name:$.name,changed:!0};return{id:n.id,name:$.name,changed:!1}}return{id:this.channels.addConnector(i,{type:"discord",name:$.name,...Q_({literal:g.token,env:g.tokenEnv})}).id,name:$.name,changed:!0}}ensureGh(i,$){let n=this.channels.getConnector(i,$.name);if(n&&n.type!=="gh")throw Error(`connector "${$.name}" exists in channel "${i}" with type "${n.type}", funnel.json declares "gh"`);if(n&&n.type==="gh"){let v=Li.parse(n);if($.pollInterval!==void 0&&v.pollInterval!==$.pollInterval)return this.channels.updateGhConnector(i,$.name,{pollInterval:$.pollInterval}),{id:v.id,name:$.name,changed:!0};return{id:v.id,name:$.name,changed:!1}}return{id:this.channels.addConnector(i,{type:"gh",name:$.name,...$.pollInterval!==void 0?{pollInterval:$.pollInterval}:{}}).id,name:$.name,changed:!0}}ensureSchedule(i,$){let n=this.channels.getConnector(i,$.name);if(n&&n.type!=="schedule")throw Error(`connector "${$.name}" exists in channel "${i}" with type "${n.type}", funnel.json declares "schedule"`);if(n&&n.type==="schedule")return{id:n.id,name:$.name,changed:!1};return{id:this.channels.addConnector(i,{type:"schedule",name:$.name}).id,name:$.name,changed:!0}}findExistingSlack(i,$){let n=this.channels.getConnector(i,$);if(!n)return null;if(n.type!=="slack")throw Error(`connector "${$}" exists in channel "${i}" with type "${n.type}", funnel.json declares "slack"`);return Ti.parse(n)}findExistingDiscord(i,$){let n=this.channels.getConnector(i,$);if(!n)return null;if(n.type!=="discord")throw Error(`connector "${$}" exists in channel "${i}" with type "${n.type}", funnel.json declares "discord"`);return Ai.parse(n)}removeExtras(i,$){let n=this.channels.get(i);if(!n)return[];let g=n.connectors.filter((v)=>!$.has(v.id));for(let v of g)this.channels.removeConnector(i,v.name);return g.map((v)=>v.name)}async resolveSlot(i){if(i.existingEnv!==void 0)return{token:void 0,tokenEnv:i.existingEnv};if(i.existingLiteral!==void 0)return{token:i.existingLiteral,tokenEnv:void 0};return{token:await this.prompter.promptSecret(i.label),tokenEnv:void 0}}}import{join as jr}from"path";var AW="bun",BW=["funnel","mcp"],lW="funnel",LW=new F,un=(i)=>{return typeof i==="object"&&i!==null&&!Array.isArray(i)};class G_{fs;constructor(i={}){this.fs=i.fs??LW,Object.freeze(this)}install(i){if(!this.fs.existsSync(i))throw Error(`repository does not exist: ${i}`);this.fs.withFileLock(jr(i,".mcp.json.lock"),()=>{let $=this.readConfig(i),n=$.mcpServers,g=un(n)?n:{},r=this.findServerName(g)??lW;g[r]={command:AW,args:BW},$.mcpServers=g,this.writeConfig(i,$)})}uninstall(i){if(!this.fs.existsSync(i))return;this.fs.withFileLock(jr(i,".mcp.json.lock"),()=>{let $=this.readConfig(i),n=$.mcpServers;if(!un(n))return;let g=this.findServerName(n);if(!g)return;delete n[g],this.writeConfig(i,$)})}findInstalledName(i){let n=this.readConfig(i).mcpServers;return un(n)?this.findServerName(n):null}findServerName(i){for(let $ of Object.entries(i))if(this.isFunnelEntry($[1]))return $[0];return null}isFunnelEntry(i){if(!un(i))return!1;let{command:$,args:n}=i;if($==="bun"&&Array.isArray(n)&&n[0]==="funnel")return!0;if($==="funnel")return!0;return!1}readConfig(i){let $=jr(i,".mcp.json");if(!this.fs.existsSync($))return{};let n=this.fs.readFileSync($).trim();if(!n)return{};let g;try{g=JSON.parse(n)}catch(v){throw Error(`invalid .mcp.json (${$}): ${v instanceof Error?v.message:String(v)}`)}if(!un(g))throw Error(`invalid .mcp.json (${$}): expected a JSON object`);return g}writeConfig(i,$){let n=jr(i,".mcp.json");this.fs.writeFileSync(n,`${JSON.stringify($,null,2)}
794
794
  `)}}var TW=new F;class Y_{store;idGenerator;fs;constructor(i){this.store=i.store,this.idGenerator=i.idGenerator,this.fs=i.fs??TW,Object.freeze(this)}list(){return this.store.read().profiles}get(i){return this.list().find(($)=>$.name===i)??null}getById(i){return this.list().find(($)=>$.id===i)??null}getDefault(){return this.list()[0]??null}add(i){this.store.update(($)=>{if($.profiles.some((n)=>n.name===i.name))throw Error(`profile "${i.name}" already exists`);if(!$.channels.some((n)=>n.id===i.channelId))throw Error(`channel id "${i.channelId}" not found`);$.profiles.push({id:this.idGenerator.generate(),name:i.name,path:i.path,channelId:i.channelId,options:i.options??[],env:i.env??{},resume:i.resume??!0})})}remove(i){this.store.update(($)=>{let n=$.profiles.findIndex((g)=>g.name===i);if(n<0)throw Error(`profile "${i}" not found`);$.profiles.splice(n,1)})}rename(i,$){this.store.update((n)=>{let g=n.profiles.find((v)=>v.name===i);if(!g)throw Error(`profile "${i}" not found`);if(n.profiles.some((v)=>v.name===$))throw Error(`profile "${$}" already exists`);g.name=$})}asDefault(i){this.store.update(($)=>{let n=$.profiles.findIndex((v)=>v.name===i);if(n<0)throw Error(`profile "${i}" not found`);if(n===0)return;let[g]=$.profiles.splice(n,1);if(!g)return;$.profiles.unshift(g)})}hasChannelRef(i){return this.store.read().profiles.some(($)=>$.channelId===i)}getSessionId(i){return this.getById(i)?.sessionId??null}setSessionId(i,$){this.store.update((n)=>{let g=n.profiles.find((v)=>v.id===i);if(!g)throw Error(`profile id "${i}" not found`);g.sessionId=$})}sessionFileExists(i,$,n){return wr({fs:this.fs,cwd:i,sessionId:$,env:n})}update(i,$){this.store.update((n)=>{let g=n.profiles.find((v)=>v.name===i);if(!g)throw Error(`profile "${i}" not found`);if($.channelId!==void 0){if(!n.channels.some((v)=>v.id===$.channelId))throw Error(`channel id "${$.channelId}" not found`);g.channelId=$.channelId}if($.path!==void 0)g.path=$.path;if($.options!==void 0)g.options=$.options;if($.env!==void 0)g.env=$.env;if($.resume!==void 0)g.resume=$.resume})}}class H_{props;constructor(i){this.props=i;Object.freeze(this)}async ensureGatewayRunning(){if(this.props.gateway.isRunning())return{ok:!0,actions:[{kind:"gateway:already-running"}],message:"gateway is already running"};if(!await this.props.gateway.start())return{ok:!1,actions:[],message:"gateway failed to start \u2014 check `fnl gateway logs`"};return{ok:!0,actions:[{kind:"gateway:started"}],message:"gateway started"}}async restartGateway(){if(!(await this.props.gateway.restart()).ok)return{ok:!1,actions:[],message:"gateway restart failed \u2014 check `fnl gateway logs`"};return{ok:!0,actions:[{kind:"gateway:restarted"}],message:"gateway restarted"}}async restartListener(i,$){let n=await this.props.listeners.restart(i,$);if(n.state==="offline")return{ok:!1,actions:[],message:"gateway is not running \u2014 call ensureGatewayRunning() first"};if(n.state==="not-found")return{ok:!1,actions:[{kind:"listener:skipped",channel:i,connector:$,reason:"not-found"}],message:`listener not found: ${i}/${$}`};if(n.state==="error")return{ok:!1,actions:[{kind:"listener:skipped",channel:i,connector:$,reason:n.reason}],message:`listener restart failed: ${n.reason}`};return{ok:!0,actions:[{kind:"listener:restarted",channel:i,connector:$}],message:`restarted ${i}/${$}`}}async restartAllDeadListeners(){let i=await this.props.listeners.list();if(i.state==="offline")return{ok:!1,actions:[],message:"gateway is not running \u2014 call ensureGatewayRunning() first"};if(i.state==="error")return{ok:!1,actions:[],message:`could not list listeners: ${i.reason}`};let $=i.listeners.filter((v)=>!v.alive);if($.length===0)return{ok:!0,actions:[],message:"no dead listeners found"};let n=[];for(let v of $){let r=await this.props.listeners.restart(v.channelName,v.name);if(r.state==="ok")n.push({kind:"listener:restarted",channel:v.channelName,connector:v.name});else{let b=r.state==="error"?r.reason:r.state;n.push({kind:"listener:skipped",channel:v.channelName,connector:v.name,reason:b})}}let g=n.filter((v)=>v.kind==="listener:restarted").length;return{ok:g>0,actions:n,message:`restarted ${g}/${$.length} dead listener(s)`}}}import{stderr as Or,stdin as Qi}from"process";class Un{}var ZW="*",qW="\r",Nk=`
795
- `,MW=String.fromCharCode(8),xW=String.fromCharCode(127),NW=String.fromCharCode(3),EW=String.fromCharCode(4);class z_ extends Un{async promptSecret(i){if(!Qi.isTTY)throw Error(`cannot prompt for "${i}": stdin is not a TTY. Set the matching env var or run \`fnl channels <ch> connectors add ...\` first.`);Or.write(`${i}: `);let $=Qi.isRaw;Qi.setRawMode(!0),Qi.resume();try{return await this.readSecret()}finally{Qi.setRawMode($),Qi.pause(),Or.write(Nk)}}readSecret(){return new Promise((i,$)=>{let n="",g=(v)=>{for(let r of v){let b=String.fromCharCode(r);if(b===Nk||b===qW){Qi.off("data",g),i(n);return}if(b===NW){Qi.off("data",g),$(Error("prompt cancelled"));return}if(b===EW){if(Qi.off("data",g),n.length===0)$(Error("prompt cancelled"));else i(n);return}if(b===MW||b===xW){if(n.length>0)n=n.slice(0,-1),Or.write("\b \b");continue}n+=b,Or.write(ZW)}};Qi.on("data",g)})}}var tW=384;class S_ extends av{dirs;files;mtimes;modes;now;constructor(i={}){super();this.dirs=new Set(i.dirs??[]),this.files=new Map(Object.entries(i.files??{})),this.mtimes=new Map(Object.entries(i.mtimes??{})),this.modes=new Map(Object.entries(i.modes??{})),this.now=i.now??(()=>Date.now())}existsSync(i){return this.dirs.has(i)||this.files.has(i)}readFileSync(i){return this.files.get(i)??""}writeFileSync(i,$){this.files.set(i,$),this.touch(i)}writeSecretFileSync(i,$){this.files.set(i,$),this.modes.set(i,tW),this.touch(i)}appendFileSync(i,$){let n=this.files.get(i)??"";this.files.set(i,n+$),this.touch(i)}unlink(i){this.files.delete(i),this.mtimes.delete(i),this.modes.delete(i)}mkdirSync(i,$){this.dirs.add(i)}readdirSync(i){let $=i.endsWith("/")?i:`${i}/`,n=[];for(let g of this.files.keys()){if(!g.startsWith($))continue;let v=g.slice($.length);if(!v.includes("/"))n.push(v)}return n}statSync(i){let $=this.mtimes.get(i);if($===void 0)throw Error(`not found: ${i}`);return{mtimeMs:$,mode:this.modes.get(i)??null}}withFileLock(i,$){return $()}setMtime(i,$){this.mtimes.set(i,$)}setMode(i,$){this.modes.set(i,$)}touch(i){if(!this.mtimes.has(i))this.mtimes.set(i,this.now());else this.mtimes.set(i,this.now())}}class A_ extends vn{counter=0;prefix;constructor(i={}){super();this.prefix=i.prefix??"id"}generate(){return this.counter++,`${this.prefix}-${this.counter}`}}class In{}class B_ extends In{file=null;entries=[];info(i,$){this.entries.push({level:"info",message:i,meta:$})}warn(i,$){this.entries.push({level:"warn",message:i,meta:$})}error(i,$){this.entries.push({level:"error",message:i,meta:$})}clear(){this.entries.length=0}}var Ek={exitCode:0,stdout:"",stderr:""};class l_ extends yv{calls=[];killed=[];handler=()=>Ek;syncHandler=()=>Ek;aliveStub=null;listStub=null;startTimeStub=null;on(i){return this.handler=i,this}onSync(i){return this.syncHandler=i,this}onIsAlive(i){return this.aliveStub=i,this}onListProcessesContaining(i){return this.listStub=i,this}onGetStartTime(i){return this.startTimeStub=i,this}async run(i,$={}){this.calls.push({kind:"run",command:i,options:$});let n=await this.handler(i);return{exitCode:n.exitCode??0,stdout:n.stdout??"",stderr:n.stderr??""}}runSync(i){this.calls.push({kind:"runSync",command:i});let $=this.syncHandler(i);return{exitCode:$.exitCode??0,stdout:$.stdout??"",stderr:$.stderr??""}}async attach(i,$={}){if(this.calls.push({kind:"attach",command:i,options:$}),$.onSpawned)$.onSpawned(1);return(await this.handler(i)).exitCode??0}detach(i,$={}){this.calls.push({kind:"detach",command:i,options:$})}kill(i,$="SIGTERM"){this.calls.push({kind:"kill",command:[String(i),$]}),this.killed.push({pid:i,signal:$})}isAlive(i){if(this.aliveStub)return this.aliveStub(i);let $=this.syncHandler(["ps","-p",String(i),"-o","state="]);if(($.exitCode??0)!==0)return!1;let n=($.stdout??"").trim();if(!n)return!1;return!n.startsWith("Z")}listProcessesContaining(i){if(this.listStub)return this.listStub(i);return[]}getStartTime(i){if(this.startTimeStub)return this.startTimeStub(i);return null}}var cW=(i={})=>({version:qi,channels:[],profiles:[],...i});class L_ extends nn{state;constructor(i){super();this.state=cW(i)}read(){return this.state}write(i){this.state=i}update(i){return i(this.state)}}class T_ extends rn{current;constructor(i={}){super();this.current=i.start??new Date(0)}now(){return new Date(this.current.getTime())}set(i){this.current=i}advance(i){this.current=new Date(this.current.getTime()+i)}}class Z_ extends Uv{calls=[];handler=()=>({status:200,body:""});on(i){return this.handler=i,this}async fetch(i){this.calls.push(i);let $=await this.handler(i),n=$.status??200,g=$.body??"";return{status:n,ok:n>=200&&n<300,text:async()=>g,json:async()=>JSON.parse(g)}}}class q_ extends Un{answers;asked=[];constructor(i={}){super();this.answers=new Map(Object.entries(i.answers??{}))}async promptSecret(i){this.asked.push(i);let $=this.answers.get(i);if($===void 0)throw Error(`no answer seeded for prompt "${i}"`);return $}}var CW=["started","connected","disconnected","auth-failed","stopped","error"],A7=D.object({event_id:D.string(),type:D.string(),connector_id:D.string().nullable(),channel_id:D.string().nullable(),payload:D.string()}),B7=D.object({event_id:D.string(),type:D.string(),connector_id:D.string().nullable(),channel_id:D.string().nullable(),outcome:D.string(),payload:D.string()}),tk=D.object({type:D.string(),connector_id:D.string().nullable(),channel_id:D.string().nullable(),status:D.enum(CW),detail:D.string()});class _n{}class N_ extends _n{now;raws=[];processeds=[];connections=[];constructor(i=()=>Date.now()){super();this.now=i;Object.freeze(this)}recordRaw(i){this.raws.push({...i,seq:this.raws.length+1,ts:this.now()})}recordProcessed(i){this.processeds.push({...i,seq:this.processeds.length+1,ts:this.now()})}recordConnection(i){this.connections.push({...i,seq:this.connections.length+1,ts:this.now()})}queryRaw(i){let $=this.raws.filter((n)=>{if(!M_(n,i))return!1;if(i.eventId!==void 0&&n.eventId!==i.eventId)return!1;return!0});return x_($,i.limit)}queryProcessed(i){let $=this.processeds.filter((n)=>{if(!M_(n,i))return!1;if(i.eventId!==void 0&&n.eventId!==i.eventId)return!1;if(i.outcome!==void 0&&n.outcome!==i.outcome)return!1;if(i.outcomePrefix!==void 0&&!n.outcome.startsWith(i.outcomePrefix))return!1;return!0});return x_($,i.limit)}queryConnection(i){let $=this.connections.filter((n)=>{if(!M_(n,i))return!1;if(i.status!==void 0&&n.status!==i.status)return!1;if(i.statuses!==void 0&&!i.statuses.includes(n.status))return!1;return!0});return x_($,i.limit)}clear(){this.raws.length=0,this.processeds.length=0,this.connections.length=0}close(){}}var M_=(i,$)=>{if($.type!==void 0&&i.type!==$.type)return!1;if($.connectorId!==void 0&&i.connectorId!==$.connectorId)return!1;if($.channelId!==void 0&&i.channelId!==$.channelId)return!1;if("seq"in i&&$.seq!==void 0&&i.seq!==$.seq)return!1;return!0},x_=(i,$)=>{if($===void 0)return i;if($<=0)return[];return i.slice(-$)};var ck=D.object({content:D.string().min(1),meta:D.record(D.string(),D.string()).optional(),connector:D.string().min(1).optional(),target:D.string().min(1).optional()}),Ck=D.object({ok:D.literal(!0),offset:D.number().int().nonnegative()});var RW={state:"offline"};class E_{port;isDaemonRunning;getToken;constructor(i){this.port=i.port,this.isDaemonRunning=i.isDaemonRunning,this.getToken=i.getToken??(()=>null),Object.freeze(this)}async publish(i,$){if(!this.isDaemonRunning())return RW;try{let n=`${xi(this.port)}/channels/${encodeURIComponent(i)}/publish`,g=await J$(n,{method:"POST",headers:{...this.authHeaders(),"content-type":"application/json"},body:JSON.stringify($)});if(!g.ok)return{state:"error",reason:await g.text()||`HTTP ${g.status}`};let v=Ck.safeParse(await g.json());if(!v.success)return{state:"error",reason:"malformed daemon response"};return{state:"ok",offset:v.data.offset}}catch(n){return{state:"error",reason:n instanceof Error?n.message:String(n)}}}authHeaders(){let i=this.getToken();return i?{authorization:`Bearer ${i}`}:{}}}import{join as Rk}from"path";import{existsSync as FW}from"fs";import{dirname as oW,resolve as t_}from"path";import{fileURLToPath as mW}from"url";var Jr=()=>{let i=oW(mW(import.meta.url)),$=[t_(i,"./daemon.ts"),t_(i,"./daemon.js"),t_(i,"./gateway/daemon.js")];for(let n of $)if(FW(n))return n;throw Error(`daemon script not found (looked in ${$.join(", ")})`)};var yW=5000,hW=2000,Fk=100,dW=200,aW=new R,pW=new F,eW=new Ni,sW=(i)=>new Promise(($)=>{setTimeout($,i)});class c_{process;fs;clock;dir;pidFile;gatewayLog;tmpDir;port;sleep;constructor(i={}){this.process=i.process??aW,this.fs=i.fs??pW,this.clock=i.clock??eW,this.dir=i.dir??ji,this.tmpDir=i.tmpDir??fi(),this.pidFile=Rk(this.dir,"gateway.pid"),this.gatewayLog=Rk(this.tmpDir,"gateway.log"),this.port=i.port??Mi(),this.sleep=i.sleep??sW,Object.freeze(this)}isRunning(){let i=this.readPid();if(!i)return!1;return this.isProcessAlive(i)}getStatus(){let i=this.readPid(),$=i!==null&&this.isProcessAlive(i);return{running:$,pid:$?i:null,port:this.port}}async start(i={}){if(this.isRunning())return!0;this.fs.mkdirSync(this.tmpDir,{recursive:!0});let $=Jr(),n=this.buildStartCommand($,i);this.process.detach(n,{env:{FUNNEL_DIR:this.dir,FUNNEL_PORT:String(this.port),FUNNEL_TMP_DIR:this.tmpDir},stdoutFile:this.gatewayLog,stderrFile:this.gatewayLog});let g=this.clock.millis()+yW;while(this.clock.millis()<g){if(this.isRunning())return!0;await this.sleep(Fk)}return this.isRunning()}buildStartCommand(i,$={}){let n=`funnel-gateway[${this.dir}]`;if($.caffeinate!==!1&&globalThis.process.platform==="darwin")return["caffeinate","-is","bun",i,n];return["bun",i,n]}async stop(){let i=this.readPid();if(!i)return!0;if(!this.isProcessAlive(i))return this.removePid(),!0;try{this.process.kill(i,"SIGTERM")}catch{return!1}let $=this.clock.millis()+hW;while(this.clock.millis()<$){if(!this.isProcessAlive(i))return this.removePid(),!0;await this.sleep(Fk)}try{this.process.kill(i,"SIGKILL")}catch{}return await this.sleep(dW),this.removePid(),!this.isProcessAlive(i)}async restart(i={}){let $=this.isRunning();if(i.onlyIfRunning&&!$)return{ok:!0,wasRunning:!1,stopped:!1,started:!1};let n=$?await this.stop():!0;if(!n)return{ok:!1,wasRunning:$,stopped:!1,started:!1};let g=await this.start({caffeinate:i.caffeinate});return{ok:g,wasRunning:$,stopped:n,started:g}}getGatewayLog(){return this.gatewayLog}getPort(){return this.port}readPid(){if(!this.fs.existsSync(this.pidFile))return null;try{let i=this.fs.readFileSync(this.pidFile).trim(),$=Number(i);if(!$||$<=0)return null;return $}catch{return null}}removePid(){this.fs.unlink(this.pidFile)}isProcessAlive(i){return this.process.isAlive(i)}}import{existsSync as sV,mkdirSync as iX}from"fs";import{dirname as $X}from"path";import{timingSafeEqual as iV}from"crypto";var Ei=(i)=>{return async($,n)=>{let r=($.req.header("authorization")??"").match(/^Bearer\s+(.+)$/i)?.[1]??"";if(!Pr(r,i.expected))return $.text("unauthorized",401);return await n()}},Pr=(i,$)=>{let n=Buffer.from(i,"utf-8"),g=Buffer.from($,"utf-8"),v=Math.max(n.length,g.length,1),r=Buffer.alloc(v),b=Buffer.alloc(v);return n.copy(r),g.copy(b),iV(r,b)&&n.length===g.length};var C_=(i,$,n)=>{return(g,v)=>{let r=-1;return b(0);async function b(u){if(u<=r)throw Error("next() called multiple times");r=u;let U,I=!1,_;if(i[u])_=i[u][0][0],g.req.routeIndex=u;else _=u===i.length&&v||void 0;if(_)try{U=await _(g,()=>b(u+1))}catch(k){if(k instanceof Error&&$)g.error=k,U=await $(k,g),I=!0;else throw k}else if(g.finalized===!1&&n)U=await n(g);if(U&&(g.finalized===!1||I))g.res=U;return g}}};var si=class extends Error{res;status;constructor(i=500,$){super($?.message,{cause:$?.cause});this.res=$?.res,this.status=i}getResponse(){if(this.res)return new Response(this.res.body,{status:this.status,headers:this.res.headers});return new Response(this.message,{status:this.status})}};var ok=Symbol();var Wr=(i,$)=>{return new Response(i,{headers:{"Content-Type":$.replace(/^[^;]+/,(g)=>g.toLowerCase())}}).formData()};var Vr=(i)=>("headers"in i),yk=async(i,$=Object.create(null))=>{let{all:n=!1,dot:g=!1}=$,b=(Vr(i)?i.headers:i.raw.headers).get("Content-Type")?.split(";")[0].trim().toLowerCase();if(b==="multipart/form-data"||b==="application/x-www-form-urlencoded")return $V(i,{all:n,dot:g});return{}};async function $V(i,$){if(!Vr(i)&&i.bodyCache.formData)return mk(await i.bodyCache.formData,$);let n=Vr(i)?i.headers:i.raw.headers,g=await i.arrayBuffer(),v=Wr(g,n.get("Content-Type")||"");if(!Vr(i))i.bodyCache.formData=v;let r=await v;if(r)return mk(r,$);return{}}function mk(i,$){let n=Object.create(null);if(i.forEach((g,v)=>{if(!($.all||v.endsWith("[]")))n[v]=g;else vV(n,v,g)}),$.dot)Object.entries(n).forEach(([g,v])=>{if(g.includes("."))nV(n,g,v),delete n[g]});return n}var vV=(i,$,n)=>{if(i[$]!==void 0)if(Array.isArray(i[$]))i[$].push(n);else i[$]=[i[$],n];else if(!$.endsWith("[]"))i[$]=n;else i[$]=[n]},nV=(i,$,n)=>{if(/(?:^|\.)__proto__\./.test($))return;let g=i,v=$.split(".");v.forEach((r,b)=>{if(b===v.length-1)g[r]=n;else{if(!g[r]||typeof g[r]!=="object"||Array.isArray(g[r])||g[r]instanceof File)g[r]=Object.create(null);g=g[r]}})};var hk=(i)=>$v(i,gn),dk=class{raw;#i;#$;routeIndex=0;path;bodyCache={};constructor(i,$="/",n=[[]]){this.raw=i,this.path=$,this.#$=n,this.#i={}}param(i){return i?this.#v(i):this.#r()}#v(i){let $=this.#$[0][this.routeIndex][1][i],n=this.#g($);return n&&/\%/.test(n)?hk(n):n}#r(){let i={},$=Object.keys(this.#$[0][this.routeIndex][1]);for(let n of $){let g=this.#g(this.#$[0][this.routeIndex][1][n]);if(g!==void 0)i[n]=/\%/.test(g)?hk(g):g}return i}#g(i){return this.#$[1]?this.#$[1][i]:i}query(i){return uk(this.url,i)}queries(i){return Uk(this.url,i)}header(i){if(i)return this.raw.headers.get(i)??void 0;let $=Object.create(null);return this.raw.headers.forEach((n,g)=>{$[g]=n}),$}async parseBody(i){return yk(this,i)}#n=(i)=>{let{bodyCache:$,raw:n}=this,g=$[i];if(g)return g;let v=Object.keys($)[0];if(v)return $[v].then((r)=>{if(v==="json")r=JSON.stringify(r);return new Response(r)[i]()});return $[i]=n[i]()};json(){return this.#n("text").then((i)=>JSON.parse(i))}text(){return this.#n("text")}arrayBuffer(){return this.#n("arrayBuffer")}bytes(){return this.#n("arrayBuffer").then((i)=>new Uint8Array(i))}blob(){return this.#n("blob")}formData(){return this.#n("formData")}addValidatedData(i,$){this.#i[i]=$}valid(i){return this.#i[i]}get url(){return this.raw.url}get method(){return this.raw.method}get[ok](){return this.#$}get matchedRoutes(){return this.#$[0].map(([[,i]])=>i)}get routePath(){return this.#$[0].map(([[,i]])=>i)[this.routeIndex].path}};var ak={Stringify:1,BeforeStream:2,Stream:3},gV=(i,$)=>{let n=new String(i);return n.isEscaped=!0,n.callbacks=$,n};var R_=async(i,$,n,g,v)=>{if(typeof i==="object"&&!(i instanceof String)){if(!(i instanceof Promise))i=i.toString();if(i instanceof Promise)i=await i}let r=i.callbacks;if(!r?.length)return Promise.resolve(i);if(v)v[0]+=i;else v=[i];let b=Promise.all(r.map((u)=>u({phase:$,buffer:v,context:g}))).then((u)=>Promise.all(u.filter(Boolean).map((U)=>R_(U,$,!1,g,v))).then(()=>v[0]));if(n)return gV(await b,r);else return b};var rV="text/plain; charset=UTF-8",F_=(i,$)=>{return{"Content-Type":i,...$}},Dn=(i,$)=>new Response(i,$),o_=class{#i;#$;env={};#v;finalized=!1;error;#r;#g;#n;#_;#U;#I;#u;#D;#k;constructor(i,$){if(this.#i=i,$)this.#g=$.executionCtx,this.env=$.env,this.#I=$.notFoundHandler,this.#k=$.path,this.#D=$.matchResult}get req(){return this.#$??=new dk(this.#i,this.#k,this.#D),this.#$}get event(){if(this.#g&&"respondWith"in this.#g)return this.#g;else throw Error("This context has no FetchEvent")}get executionCtx(){if(this.#g)return this.#g;else throw Error("This context has no ExecutionContext")}get res(){return this.#n||=Dn(null,{headers:this.#u??=new Headers})}set res(i){if(this.#n&&i){i=Dn(i.body,i);for(let[$,n]of this.#n.headers.entries()){if($==="content-type")continue;if($==="set-cookie"){let g=this.#n.headers.getSetCookie();i.headers.delete("set-cookie");for(let v of g)i.headers.append("set-cookie",v)}else i.headers.set($,n)}}this.#n=i,this.finalized=!0}render=(...i)=>{return this.#U??=($)=>this.html($),this.#U(...i)};setLayout=(i)=>this.#_=i;getLayout=()=>this.#_;setRenderer=(i)=>{this.#U=i};header=(i,$,n)=>{if(this.finalized)this.#n=Dn(this.#n.body,this.#n);let g=this.#n?this.#n.headers:this.#u??=new Headers;if($===void 0)g.delete(i);else if(n?.append)g.append(i,$);else g.set(i,$)};status=(i)=>{this.#r=i};set=(i,$)=>{this.#v??=new Map,this.#v.set(i,$)};get=(i)=>{return this.#v?this.#v.get(i):void 0};get var(){if(!this.#v)return{};return Object.fromEntries(this.#v)}#b(i,$,n){let g=this.#n?new Headers(this.#n.headers):this.#u??new Headers;if(typeof $==="object"&&"headers"in $){let r=$.headers instanceof Headers?$.headers:new Headers($.headers);for(let[b,u]of r)if(b.toLowerCase()==="set-cookie")g.append(b,u);else g.set(b,u)}if(n)for(let[r,b]of Object.entries(n))if(typeof b==="string")g.set(r,b);else{g.delete(r);for(let u of b)g.append(r,u)}let v=typeof $==="number"?$:$?.status??this.#r;return Dn(i,{status:v,headers:g})}newResponse=(...i)=>this.#b(...i);body=(i,$,n)=>this.#b(i,$,n);text=(i,$,n)=>{return!this.#u&&!this.#r&&!$&&!n&&!this.finalized?new Response(i):this.#b(i,$,F_(rV,n))};json=(i,$,n)=>{return this.#b(JSON.stringify(i),$,F_("application/json",n))};html=(i,$,n)=>{let g=(v)=>this.#b(v,$,F_("text/html; charset=UTF-8",n));return typeof i==="object"?R_(i,ak.Stringify,!1,{}).then(g):g(i)};redirect=(i,$)=>{let n=String(i);return this.header("Location",!/[^\x00-\xFF]/.test(n)?n:encodeURI(n)),this.newResponse(null,$??302)};notFound=()=>{return this.#I??=()=>Dn(),this.#I(this)}};var x="ALL",pk="all",ek=["get","post","put","delete","options","patch"],Xr="Can not add a route since the matcher is already built.",fr=class extends Error{};var sk="__COMPOSED_HANDLER";var bV=(i)=>{return i.text("404 Not Found",404)},i2=(i,$)=>{if("getResponse"in i){let n=i.getResponse();return $.newResponse(n.body,n)}return console.error(i),$.text("Internal Server Error",500)},$2=class i{get;post;put;delete;options;patch;all;on;use;router;getPath;_basePath="/";#i="/";routes=[];constructor($={}){[...ek,pk].forEach((r)=>{this[r]=(b,...u)=>{if(typeof b==="string")this.#i=b;else this.#r(r,this.#i,b);return u.forEach((U)=>{this.#r(r,this.#i,U)}),this}}),this.on=(r,b,...u)=>{for(let U of[b].flat()){this.#i=U;for(let I of[r].flat())u.map((_)=>{this.#r(I.toUpperCase(),this.#i,_)})}return this},this.use=(r,...b)=>{if(typeof r==="string")this.#i=r;else this.#i="*",b.unshift(r);return b.forEach((u)=>{this.#r(x,this.#i,u)}),this};let{strict:g,...v}=$;Object.assign(this,v),this.getPath=g??!0?$.getPath??r_:rk}#$(){let $=new i({router:this.router,getPath:this.getPath});return $.errorHandler=this.errorHandler,$.#v=this.#v,$.routes=this.routes,$}#v=bV;errorHandler=i2;route($,n){let g=this.basePath($);return n.routes.map((v)=>{let r;if(n.errorHandler===i2)r=v.handler;else r=async(b,u)=>(await C_([],n.errorHandler)(b,()=>v.handler(b,u))).res,r[sk]=v.handler;g.#r(v.method,v.path,r,v.basePath)}),this}basePath($){let n=this.#$();return n._basePath=ei(this._basePath,$),n}onError=($)=>{return this.errorHandler=$,this};notFound=($)=>{return this.#v=$,this};mount($,n,g){let v,r;if(g)if(typeof g==="function")r=g;else if(r=g.optionHandler,g.replaceRequest===!1)v=(U)=>U;else v=g.replaceRequest;let b=r?(U)=>{let I=r(U);return Array.isArray(I)?I:[I]}:(U)=>{let I=void 0;try{I=U.executionCtx}catch{}return[U.env,I]};v||=(()=>{let U=ei(this._basePath,$),I=U==="/"?0:U.length;return(_)=>{let k=new URL(_.url);return k.pathname=this.getPath(_).slice(I)||"/",new Request(k,_)}})();let u=async(U,I)=>{let _=await n(v(U.req.raw),...b(U));if(_)return _;await I()};return this.#r(x,ei($,"*"),u),this}#r($,n,g,v){$=$.toUpperCase(),n=ei(this._basePath,n);let r={basePath:v!==void 0?ei(this._basePath,v):this._basePath,path:n,method:$,handler:g};this.router.add($,n,[g,r]),this.routes.push(r)}#g($,n){if($ instanceof Error)return this.errorHandler($,n);throw $}#n($,n,g,v){if(v==="HEAD")return(async()=>new Response(null,await this.#n($,n,g,"GET")))();let r=this.getPath($,{env:g}),b=this.router.match(v,r),u=new o_($,{path:r,matchResult:b,env:g,executionCtx:n,notFoundHandler:this.#v});if(b[0].length===1){let I;try{I=b[0][0][0][0](u,async()=>{u.res=await this.#v(u)})}catch(_){return this.#g(_,u)}return I instanceof Promise?I.then((_)=>_||(u.finalized?u.res:this.#v(u))).catch((_)=>this.#g(_,u)):I??this.#v(u)}let U=C_(b[0],this.errorHandler,this.#v);return(async()=>{try{let I=await U(u);if(!I.finalized)throw Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");return I.res}catch(I){return this.#g(I,u)}})()}fetch=($,...n)=>{return this.#n($,n[1],n[0],$.method)};request=($,n,g,v)=>{if($ instanceof Request)return this.fetch(n?new Request($,n):$,g,v);return $=$.toString(),this.fetch(new Request(/^https?:\/\//.test($)?$:`http://localhost${ei("/",$)}`,n),g,v)};fire=()=>{addEventListener("fetch",($)=>{$.respondWith(this.#n($.request,$,void 0,$.request.method))})}};var kn=[];function Qr(i,$){let n=this.buildAllMatchers(),g=(v,r)=>{let b=n[v]||n[x],u=b[2][r];if(u)return u;let U=r.match(b[0]);if(!U)return[[],kn];let I=U.indexOf("",1);return[b[1][I],U]};return this.match=g,g(i,$)}var Kr="[^/]+",wn=".*",jn="(?:|/.*)",W$=Symbol(),uV=new Set(".\\+*[^]$()");function UV(i,$){if(i.length===1)return $.length===1?i<$?-1:1:-1;if($.length===1)return 1;if(i===wn||i===jn)return 1;else if($===wn||$===jn)return-1;if(i===Kr)return 1;else if($===Kr)return-1;return i.length===$.length?i<$?-1:1:$.length-i.length}var v2=class i{#i;#$;#v=Object.create(null);insert($,n,g,v,r){if($.length===0){if(this.#i!==void 0)throw W$;if(r)return;this.#i=n;return}let[b,...u]=$,U=b==="*"?u.length===0?["","",wn]:["","",Kr]:b==="/*"?["","",jn]:b.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/),I;if(U){let _=U[1],k=U[2]||Kr;if(_&&U[2]){if(k===".*")throw W$;if(k=k.replace(/^\((?!\?:)(?=[^)]+\)$)/,"(?:"),/\((?!\?:)/.test(k))throw W$}if(I=this.#v[k],!I){if(Object.keys(this.#v).some((O)=>O!==wn&&O!==jn))throw W$;if(r)return;if(I=this.#v[k]=new i,_!=="")I.#$=v.varIndex++}if(!r&&_!=="")g.push([_,I.#$])}else if(I=this.#v[b],!I){if(Object.keys(this.#v).some((_)=>_.length>1&&_!==wn&&_!==jn))throw W$;if(r)return;I=this.#v[b]=new i}I.insert(u,n,g,v,r)}buildRegExpStr(){let n=Object.keys(this.#v).sort(UV).map((g)=>{let v=this.#v[g];return(typeof v.#$==="number"?`(${g})@${v.#$}`:uV.has(g)?`\\${g}`:g)+v.buildRegExpStr()});if(typeof this.#i==="number")n.unshift(`#${this.#i}`);if(n.length===0)return"";if(n.length===1)return n[0];return"(?:"+n.join("|")+")"}};var n2=class{#i={varIndex:0};#$=new v2;insert(i,$,n){let g=[],v=[];for(let b=0;;){let u=!1;if(i=i.replace(/\{[^}]+\}/g,(U)=>{let I=`@\\${b}`;return v[b]=[I,U],b++,u=!0,I}),!u)break}let r=i.match(/(?::[^\/]+)|(?:\/\*$)|./g)||[];for(let b=v.length-1;b>=0;b--){let[u]=v[b];for(let U=r.length-1;U>=0;U--)if(r[U].indexOf(u)!==-1){r[U]=r[U].replace(u,v[b][1]);break}}return this.#$.insert(r,$,g,this.#i,n),g}buildRegExp(){let i=this.#$.buildRegExpStr();if(i==="")return[/^$/,[],[]];let $=0,n=[],g=[];return i=i.replace(/#(\d+)|@(\d+)|\.\*\$/g,(v,r,b)=>{if(r!==void 0)return n[++$]=Number(r),"$()";if(b!==void 0)return g[Number(b)]=++$,"";return""}),[new RegExp(`^${i}`),n,g]}};var IV=[/^$/,[],Object.create(null)],g2=Object.create(null);function r2(i){return g2[i]??=new RegExp(i==="*"?"":`^${i.replace(/\/\*$|([.\\+*[^\]$()])/g,($,n)=>n?`\\${n}`:"(?:|/.*)")}$`)}function _V(){g2=Object.create(null)}function DV(i){let $=new n2,n=[];if(i.length===0)return IV;let g=i.map((I)=>[!/\*|\/:/.test(I[0]),...I]).sort(([I,_],[k,O])=>I?1:k?-1:_.length-O.length),v=Object.create(null);for(let I=0,_=-1,k=g.length;I<k;I++){let[O,J,Q]=g[I];if(O)v[J]=[Q.map(([z])=>[z,Object.create(null)]),kn];else _++;let A;try{A=$.insert(J,_,O)}catch(z){throw z===W$?new fr(J):z}if(O)continue;n[_]=Q.map(([z,B])=>{let Ii=Object.create(null);B-=1;for(;B>=0;B--){let[y,N]=A[B];Ii[y]=N}return[z,Ii]})}let[r,b,u]=$.buildRegExp();for(let I=0,_=n.length;I<_;I++)for(let k=0,O=n[I].length;k<O;k++){let J=n[I][k]?.[1];if(!J)continue;let Q=Object.keys(J);for(let A=0,z=Q.length;A<z;A++)J[Q[A]]=u[J[Q[A]]]}let U=[];for(let I in b)U[I]=n[b[I]];return[r,U,v]}function gv(i,$){if(!i)return;for(let n of Object.keys(i).sort((g,v)=>v.length-g.length))if(r2(n).test($))return[...i[n]];return}var Gr=class{name="RegExpRouter";#i;#$;constructor(){this.#i={[x]:Object.create(null)},this.#$={[x]:Object.create(null)}}add(i,$,n){let g=this.#i,v=this.#$;if(!g||!v)throw Error(Xr);if(!g[i])[g,v].forEach((u)=>{u[i]=Object.create(null),Object.keys(u[x]).forEach((U)=>{u[i][U]=[...u[x][U]]})});if($==="/*")$="*";let r=($.match(/\/:/g)||[]).length;if(/\*$/.test($)){let u=r2($);if(i===x)Object.keys(g).forEach((U)=>{g[U][$]||=gv(g[U],$)||gv(g[x],$)||[]});else g[i][$]||=gv(g[i],$)||gv(g[x],$)||[];Object.keys(g).forEach((U)=>{if(i===x||i===U)Object.keys(g[U]).forEach((I)=>{u.test(I)&&g[U][I].push([n,r])})}),Object.keys(v).forEach((U)=>{if(i===x||i===U)Object.keys(v[U]).forEach((I)=>u.test(I)&&v[U][I].push([n,r]))});return}let b=Ir($)||[$];for(let u=0,U=b.length;u<U;u++){let I=b[u];Object.keys(v).forEach((_)=>{if(i===x||i===_)v[_][I]||=[...gv(g[_],I)||gv(g[x],I)||[]],v[_][I].push([n,r-U+u+1])})}}match=Qr;buildAllMatchers(){let i=Object.create(null);return Object.keys(this.#$).concat(Object.keys(this.#i)).forEach(($)=>{i[$]||=this.#v($)}),this.#i=this.#$=void 0,_V(),i}#v(i){let $=[],n=i===x;if([this.#i,this.#$].forEach((g)=>{let v=g[i]?Object.keys(g[i]).map((r)=>[r,g[i][r]]):[];if(v.length!==0)n||=!0,$.push(...v);else if(i!==x)$.push(...Object.keys(g[x]).map((r)=>[r,g[x][r]]))}),!n)return null;else return DV($)}};var kV=class{name="PreparedRegExpRouter";#i;#$;constructor(i,$){this.#i=i,this.#$=$}#v(i,$){let n=this.#i[i];n[1].forEach((g)=>g&&g.push($)),Object.values(n[2]).forEach((g)=>g[0].push($))}#r(i,$,n,g,v){let r=this.#i[i];if(!v)r[2][$][0].push([n,{}]);else g.forEach((b)=>{if(typeof b==="number")r[1][b].push([n,v]);else r[2][b||$][0].push([n,v])})}add(i,$,n){if(!this.#i[i]){let v=this.#i[x],r={};for(let b in v[2])r[b]=[v[2][b][0].slice(),kn];this.#i[i]=[v[0],v[1].map((b)=>Array.isArray(b)?b.slice():0),r]}if($==="/*"||$==="*"){let v=[n,{}];if(i===x)for(let r in this.#i)this.#v(r,v);else this.#v(i,v);return}let g=this.#$[$];if(!g)throw Error(`Path ${$} is not registered`);for(let[v,r]of g)if(i===x)for(let b in this.#i)this.#r(b,$,n,v,r);else this.#r(i,$,n,v,r)}buildAllMatchers(){return this.#i}match=Qr};var m_=class{name="SmartRouter";#i=[];#$=[];constructor(i){this.#i=i.routers}add(i,$,n){if(!this.#$)throw Error(Xr);this.#$.push([i,$,n])}match(i,$){if(!this.#$)throw Error("Fatal error");let n=this.#i,g=this.#$,v=n.length,r=0,b;for(;r<v;r++){let u=n[r];try{for(let U=0,I=g.length;U<I;U++)u.add(...g[U]);b=u.match(i,$)}catch(U){if(U instanceof fr)continue;throw U}this.match=u.match.bind(u),this.#i=[u],this.#$=void 0;break}if(r===v)throw Error("Fatal error");return this.name=`SmartRouter + ${this.activeRouter.name}`,b}get activeRouter(){if(this.#$||this.#i.length!==1)throw Error("No active router has been determined yet.");return this.#i[0]}};var On=Object.create(null),wV=(i)=>{for(let $ in i)return!0;return!1},b2=class i{#i;#$;#v;#r=0;#g=On;constructor($,n,g){if(this.#$=g||Object.create(null),this.#i=[],$&&n){let v=Object.create(null);v[$]={handler:n,possibleKeys:[],score:0},this.#i=[v]}this.#v=[]}insert($,n,g){this.#r=++this.#r;let v=this,r=nk(n),b=[];for(let u=0,U=r.length;u<U;u++){let I=r[u],_=r[u+1],k=gk(I,_),O=Array.isArray(k)?k[0]:I;if(O in v.#$){if(v=v.#$[O],k)b.push(k[1]);continue}if(v.#$[O]=new i,k)v.#v.push(k),b.push(k[1]);v=v.#$[O]}return v.#i.push({[$]:{handler:g,possibleKeys:b.filter((u,U,I)=>I.indexOf(u)===U),score:this.#r}}),v}#n($,n,g,v,r){for(let b=0,u=n.#i.length;b<u;b++){let U=n.#i[b],I=U[g]||U[x],_={};if(I!==void 0){if(I.params=Object.create(null),$.push(I),v!==On||r&&r!==On)for(let k=0,O=I.possibleKeys.length;k<O;k++){let J=I.possibleKeys[k],Q=_[I.score];I.params[J]=r?.[J]&&!Q?r[J]:v[J]??r?.[J],_[I.score]=!0}}}}search($,n){let g=[];this.#g=On;let r=[this],b=g_(n),u=[],U=b.length,I=null;for(let _=0;_<U;_++){let k=b[_],O=_===U-1,J=[];for(let A=0,z=r.length;A<z;A++){let B=r[A],Ii=B.#$[k];if(Ii)if(Ii.#g=B.#g,O){if(Ii.#$["*"])this.#n(g,Ii.#$["*"],$,B.#g);this.#n(g,Ii,$,B.#g)}else J.push(Ii);for(let y=0,N=B.#v.length;y<N;y++){let C=B.#v[y],ui=B.#g===On?{}:{...B.#g};if(C==="*"){let i$=B.#$["*"];if(i$)this.#n(g,i$,$,B.#g),i$.#g=ui,J.push(i$);continue}let[Wn,Vn,bv]=C;if(!k&&!(bv instanceof RegExp))continue;let _i=B.#$[Wn];if(bv instanceof RegExp){if(I===null){I=Array(U);let fn=n[0]==="/"?1:0;for(let uv=0;uv<U;uv++)I[uv]=fn,fn+=b[uv].length+1}let i$=n.substring(I[_]),Xn=bv.exec(i$);if(Xn){if(ui[Vn]=Xn[0],this.#n(g,_i,$,B.#g,ui),Xn[0].length===i$.length&&_i.#$["*"])this.#n(g,_i.#$["*"],$,B.#g,ui);if(wV(_i.#$)){_i.#g=ui;let fn=Xn[0].match(/\//)?.length??0;(u[fn]||=[]).push(_i)}continue}}if(bv===!0||bv.test(k))if(ui[Vn]=k,O){if(this.#n(g,_i,$,ui,B.#g),_i.#$["*"])this.#n(g,_i.#$["*"],$,ui,B.#g)}else _i.#g=ui,J.push(_i)}}let Q=u.shift();r=Q?J.concat(Q):J}if(g.length>1)g.sort((_,k)=>{return _.score-k.score});return[g.map(({handler:_,params:k})=>[_,k])]}};var y_=class{name="TrieRouter";#i;constructor(){this.#i=new b2}add(i,$,n){let g=Ir($);if(g){for(let v=0,r=g.length;v<r;v++)this.#i.insert(i,g[v],n);return}this.#i.insert(i,$,n)}match(i,$){return this.#i.search(i,$)}};var Jn=class extends $2{constructor(i={}){super(i);this.router=i.router??new m_({routers:[new Gr,new y_]})}};var jV=class{initApp;#i;constructor(i){this.initApp=i?.initApp,this.#i=i?.defaultAppOptions}createApp=(i)=>{let $=new Jn(i&&this.#i?{...this.#i,...i}:i??this.#i);if(this.initApp)this.initApp($);return $};createMiddleware=(i)=>i;createHandlers=(...i)=>{return i.filter(($)=>$!==void 0)}},u2=(i)=>new jV(i);var c=u2();var U2=(i)=>{let $=Buffer.byteLength(i.content,"utf-8");if(i.meta)for(let[n,g]of Object.entries(i.meta))$+=Buffer.byteLength(n,"utf-8")+Buffer.byteLength(g,"utf-8");return $},OV=1048576,JV=200,PV=4194304,WV=()=>{};class h_{clients=new Map;subscribers=new Set;logger;onError;maxBufferedBytes;now;replayBufferSize;replayBufferMaxBytes;replayBuffer=[];persistentReplay;exclusiveCursor=new Map;replayBufferBytes=0;eventsBroadcast=0;droppedSlowClients=0;lastBroadcastAt=null;latestOffset=0;constructor(i={}){this.logger=i.logger,this.onError=i.onError??WV,this.maxBufferedBytes=i.maxBufferedBytes??OV,this.now=i.now??(()=>Date.now()),this.replayBufferSize=Math.max(0,i.replayBufferSize??JV),this.replayBufferMaxBytes=Math.max(0,i.replayBufferMaxBytes??PV),this.persistentReplay=i.persistentReplay??null}getMetrics(){return{clients:this.clients.size,subscribers:this.subscribers.size,eventsBroadcast:this.eventsBroadcast,droppedSlowClients:this.droppedSlowClients,lastBroadcastAt:this.lastBroadcastAt?new Date(this.lastBroadcastAt).toISOString():null,latestOffset:this.latestOffset,oldestReplayableOffset:this.replayBuffer[0]?.offset??null}}replaySince(i,$){let n=this.replayBuffer[0]?.offset,g=this.persistentReplay&&(n===void 0||i<n-1),v=this.replayBuffer.filter((U)=>U.offset>i&&this.matchesClient(U,$));if(!g)return v;let r=this.persistentReplay?this.persistentReplay.loadSince(i).filter((U)=>this.matchesClient(U,$)):[],b=n??Number.POSITIVE_INFINITY;return[...r.filter((U)=>U.offset<b),...v]}matchesClient(i,$){let n=i.meta?.target;if(n&&n!==$.subscriberId)return!1;let g=i.meta?.channelId;if(g&&g!==$.channel)return!1;let v=i.meta?.connector;if(!v)return!0;return $.connectors.includes(v)}pickRecipients(i){let $=new Map,n=[];for(let[g,v]of this.clients){if(!this.matchesClient(i,v))continue;if(v.delivery==="exclusive"){let r=$.get(v.channel)??[];r.push(g),$.set(v.channel,r);continue}n.push(g)}for(let[g,v]of $){if(v.length===0)continue;let r=this.exclusiveCursor.get(g)??0,b=v[r%v.length];if(b)n.push(b);this.exclusiveCursor.set(g,r+1)}return n}addClient(i,$){this.clients.set(i,$)}removeClient(i){this.clients.delete(i)}getClientCount(){return this.clients.size}listChannels(){return[...this.clients.values()].map((i)=>({...i}))}subscribe(i){return this.subscribers.add(i),()=>{this.subscribers.delete(i)}}broadcast(i,$){this.latestOffset+=1;let n={content:i,meta:$,offset:this.latestOffset},g=JSON.stringify(n);if(this.eventsBroadcast+=1,this.lastBroadcastAt=this.now(),this.replayBufferSize>0){let r=U2(n);this.replayBuffer.push(n),this.replayBufferBytes+=r;while((this.replayBuffer.length>this.replayBufferSize||this.replayBufferBytes>this.replayBufferMaxBytes)&&this.replayBuffer.length>0){let b=this.replayBuffer.shift();if(b)this.replayBufferBytes-=U2(b)}}let v=this.pickRecipients(n);for(let r of v){let b=r.getBufferedAmount();if(b>this.maxBufferedBytes){let u=this.clients.get(r);this.logger?.warn("dropping slow WS client (backpressure)",{channel:u?.channel,buffered:b,max:this.maxBufferedBytes});try{r.close(1009,"backpressure")}catch{}this.clients.delete(r),this.droppedSlowClients+=1;continue}try{r.send(g)}catch(u){let U=u instanceof Error?u:Error(String(u)),I=this.clients.get(r);this.logger?.warn("ws.send failed; dropping client",{channel:I?.channel,error:U.message}),this.clients.delete(r)}}for(let r of this.subscribers){let b=(u)=>{let U=u instanceof Error?u:Error(String(u));this.logger?.error("broadcast subscriber threw",{error:U.message}),this.onError(U,{component:"broadcaster.subscriber",offset:n.offset,connector:n.meta?.connector??null,channel:n.meta?.channel??null})};try{let u=r(n);if(u instanceof Promise)u.catch(b)}catch(u){b(u)}}return n}seedLatestOffset(i){if(i>this.latestOffset)this.latestOffset=i}}var H3=D.object({type:D.string(),content:D.string(),channel_id:D.string().nullable(),connector_id:D.string().nullable(),meta:D.record(D.string(),D.string()).nullable()});class d_{}import{Database as VV}from"bun:sqlite";import{existsSync as XV,mkdirSync as fV}from"fs";import{dirname as QV}from"path";var KV=/^[a-z_][a-z0-9_]*$/,GV=500,YV=new Set(["seq","ts","type","event"]),I2=[["CREATE TABLE IF NOT EXISTS logs (seq INTEGER PRIMARY KEY, ts INTEGER NOT NULL, type TEXT, event TEXT NOT NULL)","CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts)","CREATE INDEX IF NOT EXISTS idx_logs_type ON logs (type)"],["DROP TABLE IF EXISTS leuco_log","CREATE TABLE IF NOT EXISTS logs (seq INTEGER PRIMARY KEY, ts INTEGER NOT NULL, type TEXT, event TEXT NOT NULL)","CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts)","CREATE INDEX IF NOT EXISTS idx_logs_type ON logs (type)"]];class V${db;maxRows;maxAgeMs;maxBytes;targetBytes;now;indexes;extractIndexes;insertStmt;insertWithSeqStmt;maxSeqStmt;countStmt;trimRowsStmt;trimAgeStmt;trimOldestStmt;insertsSinceByteCheck=0;constructor(i){if(i.path!==":memory:"){let r=QV(i.path);if(!XV(r))fV(r,{recursive:!0})}if(this.db=new VV(i.path),this.db.run("PRAGMA journal_mode = WAL"),this.migrate(),this.maxRows=i.maxRows??null,this.maxAgeMs=i.maxAgeMs??null,this.maxBytes=i.maxBytes??null,this.targetBytes=i.targetBytes??(i.maxBytes!==void 0?Math.floor(i.maxBytes/4):null),this.now=i.now??(()=>Date.now()),this.indexes=i.indexes??[],this.indexes.length>0)zV(this.indexes),this.extractIndexes=i.extractIndexes??null,this.syncIndexColumns();else this.extractIndexes=null;let $=["ts","type","event",...this.indexes],n=$.map(()=>"?").join(", ");this.insertStmt=this.db.prepare(`INSERT INTO logs (${$.join(", ")}) VALUES (${n})`);let g=["seq",...$],v=g.map(()=>"?").join(", ");this.insertWithSeqStmt=this.db.prepare(`INSERT INTO logs (${g.join(", ")}) VALUES (${v})`),this.maxSeqStmt=this.db.prepare("SELECT COALESCE(MAX(seq), 0) AS max FROM logs"),this.countStmt=this.db.prepare("SELECT COUNT(*) AS n FROM logs"),this.trimRowsStmt=this.db.prepare("DELETE FROM logs WHERE seq <= (SELECT seq FROM logs ORDER BY seq DESC LIMIT 1 OFFSET ?)"),this.trimAgeStmt=this.db.prepare("DELETE FROM logs WHERE ts < ?"),this.trimOldestStmt=this.db.prepare("DELETE FROM logs WHERE seq IN (SELECT seq FROM logs ORDER BY seq ASC LIMIT ?)")}insert(i){try{let $=this.buildInsertParams(i.ts,i.event),n=this.insertStmt.run(...$),g=Number(n.lastInsertRowid);return this.trim(),{seq:g,ts:i.ts,event:i.event}}catch($){return $ instanceof Error?$:Error(String($))}}insertMany(i){if(i.length===0)return[];try{let $=[];return this.db.transaction((g)=>{for(let v of g){let r=this.buildInsertParams(v.ts,v.event),b=this.insertStmt.run(...r);$.push({seq:Number(b.lastInsertRowid),ts:v.ts,event:v.event})}})(i),this.trim(),$}catch($){return $ instanceof Error?$:Error(String($))}}write(i){try{let $=[i.seq,...this.buildInsertParams(i.ts,i.event)];this.insertWithSeqStmt.run(...$),this.trim()}catch($){return $ instanceof Error?$:Error(String($))}}getMaxSeq(){let i=this.maxSeqStmt.get();return i?i.max:0}query(i={}){let $=[],n=[];if(i.seq!==void 0)$.push("seq = ?"),n.push(i.seq);else $.push("seq > ?"),n.push(i.sinceSeq??0);if(typeof i.type==="string")$.push("type = ?"),n.push(i.type);if(i.where)this.appendWhereConditions(i.where,$,n);if(i.wherePrefix)this.appendPrefixConditions(i.wherePrefix,$,n);if(i.whereIn)this.appendSetConditions(i.whereIn,$,n);let g=i.limit??1000;n.push(g);let v=i.order==="desc"?"DESC":"ASC",r=`SELECT seq, ts, type, event FROM logs WHERE ${$.join(" AND ")} ORDER BY seq ${v} LIMIT ?`,u=this.db.prepare(r).all(...n);if(v==="DESC")u.reverse();return u.map(AV)}getSchemaVersion(){return this.db.prepare("PRAGMA user_version").get()?.user_version??0}close(){this.db.close()}buildInsertParams(i,$){let n=SV($),g=JSON.stringify($);if(this.indexes.length===0)return[i,n,g];let v=this.extractIndexes?this.extractIndexes($):null,r=this.indexes.map((b)=>v?.[b]??null);return[i,n,g,...r]}appendWhereConditions(i,$,n){for(let g of this.indexKeys()){let v=i[g];if(v===void 0)continue;if(v===null)$.push(`${g} IS NULL`);else $.push(`${g} = ?`),n.push(v)}}appendPrefixConditions(i,$,n){for(let g of this.indexKeys()){let v=i[g];if(v===void 0)continue;$.push(`${g} LIKE ? ESCAPE '\\'`),n.push(`${HV(v)}%`)}}appendSetConditions(i,$,n){for(let g of this.indexKeys()){let v=i[g];if(v===void 0)continue;if(v.length===0){$.push("1 = 0");continue}let r=v.filter((U)=>U!==null),b=r.length!==v.length,u=[];if(r.length>0)u.push(`${g} IN (${r.map(()=>"?").join(", ")})`),n.push(...r);if(b)u.push(`${g} IS NULL`);$.push(`(${u.join(" OR ")})`)}}trim(){if(this.maxRows!==null){let i=this.countStmt.get();if(i&&i.n>this.maxRows)this.trimRowsStmt.run(this.maxRows)}if(this.maxAgeMs!==null)this.trimAgeStmt.run(this.now()-this.maxAgeMs);this.maybeTrimBytes()}indexKeys(){return this.indexes}maybeTrimBytes(){if(this.maxBytes===null||this.targetBytes===null)return;if(this.insertsSinceByteCheck+=1,this.insertsSinceByteCheck<GV)return;this.insertsSinceByteCheck=0;let i=this.byteSize();if(i<=this.maxBytes)return;let $=this.countStmt.get()?.n??0;if($===0)return;let n=i-this.targetBytes,g=i/$,v=Math.min($,Math.ceil(n/g));this.trimOldestStmt.run(v),this.db.run("VACUUM")}byteSize(){let i=this.db.prepare("PRAGMA page_count").get()?.n??0,$=this.db.prepare("PRAGMA page_size").get()?.n??0;return i*$}clear(){this.db.run("DELETE FROM logs"),this.db.run("VACUUM"),this.insertsSinceByteCheck=0}syncIndexColumns(){let i=new Set(this.db.prepare("PRAGMA table_info(logs)").all().map(($)=>$.name));for(let $ of this.indexes){if(!i.has($))this.db.run(`ALTER TABLE logs ADD COLUMN ${$} TEXT`);this.db.run(`CREATE INDEX IF NOT EXISTS idx_logs_${$} ON logs (${$})`)}}migrate(){let $=this.db.prepare("PRAGMA user_version").get()?.user_version??0;if($>=I2.length)return;let n=I2.slice($),g=$;for(let v of n)g+=1,this.db.transaction(()=>{for(let b of v)this.db.run(b);this.db.run(`PRAGMA user_version = ${g}`)})()}}function HV(i){return i.replaceAll("\\","\\\\").replaceAll("%","\\%").replaceAll("_","\\_")}function zV(i){for(let $ of i){if(!KV.test($))throw Error(`invalid index column name: ${$}`);if(YV.has($))throw Error(`reserved index column name: ${$}`)}}function SV(i){if(typeof i!=="object"||i===null)return null;if(!("type"in i))return null;let $=i.type;return typeof $==="string"?$:null}function AV(i){return{seq:i.seq,ts:i.ts,event:JSON.parse(i.event)}}var _2=2000;class a_ extends d_{sink;now;logger;onError;constructor(i){super();this.now=i.now??(()=>Date.now()),this.logger=i.logger,this.onError=i.onError,this.sink=new V$({path:i.path,indexes:["channel_id","connector_id"],extractIndexes:($)=>({channel_id:$.channel_id,connector_id:$.connector_id}),now:this.now,...i.maxRows!==void 0?{maxRows:i.maxRows}:{},...i.maxAgeMs!==void 0?{maxAgeMs:i.maxAgeMs}:{},...i.maxBytes!==void 0?{maxBytes:i.maxBytes}:{},...i.targetBytes!==void 0?{targetBytes:i.targetBytes}:{}})}record(i){let $={type:i.meta?.event_type??"unknown",content:BV(i.content),channel_id:i.channelId,connector_id:i.connectorId,meta:i.meta},n=this.sink.write({seq:i.offset,ts:this.now(),event:$});if(n instanceof Error)this.logger?.error("event log write failed",{offset:i.offset,error:n.message}),this.onError?.(n,{component:"sqlite-event-log",op:"record",offset:i.offset,channelId:i.channelId,connectorId:i.connectorId})}loadSince(i){let $=this.sink.query({sinceSeq:i,limit:Number.MAX_SAFE_INTEGER}),n=[];for(let g of $)n.push({content:g.event.content,meta:g.event.meta??void 0,offset:g.seq});return n}loadForChannel(i){let $={channel_id:i.channelId};if(i.connectorId!==void 0)$.connector_id=i.connectorId;let n=this.sink.query({where:$,...i.sinceSeq!==void 0?{sinceSeq:i.sinceSeq}:{},...i.limit!==void 0?{limit:i.limit}:{}}),g=[];for(let v of n)g.push({content:v.event.content,meta:v.event.meta??void 0,offset:v.seq});return g}findMaxOffset(){return this.sink.getMaxSeq()}clear(){this.sink.clear()}close(){this.sink.close()}}function BV(i){if(i.length<=_2)return i;return`${i.slice(0,_2)}...`}var lV=()=>{},LV=30000,TV=60000,ZV=30000,qV=(i)=>new Promise(($)=>{setTimeout($,i)});class ti{channels;notify;logger;onError;running=new Map;failureCounts=new Map;starting=new Set;stats=new Map;healthCheckIntervalMs;maxBackoffMs;startTimeoutMs;sleep;now;healthCheckTimer=null;healthCheckInFlight=!1;pendingRetry=new Map;constructor(i){this.channels=i.channels,this.notify=i.notify,this.logger=i.logger,this.onError=i.onError??lV,this.healthCheckIntervalMs=i.healthCheckIntervalMs??LV,this.maxBackoffMs=i.maxBackoffMs??TV,this.startTimeoutMs=i.startTimeoutMs??ZV,this.sleep=i.sleep??qV,this.now=i.now??(()=>Date.now())}static keyOf(i,$){return`${i}/${$}`}isRunning(i,$){return this.running.has(ti.keyOf(i,$))}list(){return[...this.running.entries()].map(([i,$])=>{let n=this.stats.get(i);return{channelName:$.channelName,channelId:$.channelId,name:$.config.name,type:$.config.type,alive:$.listener.isAlive(),events:n?.events??0,errors:n?.errors??0,failureCount:this.failureCounts.get(i)??0,lastEventAt:n?.lastEventAt??null}})}async start(i,$){let n=ti.keyOf(i,$);if(this.running.has(n))return{ok:!0,reason:"already running"};if(this.starting.has(n))return{ok:!1,reason:"already starting",retriable:!0};this.starting.add(n);try{return await this.startLocked(i,$,n)}finally{this.starting.delete(n)}}async startLocked(i,$,n){let g=this.channels.createListener(i,$);if(!g)return{ok:!1,reason:`connector "${$}" not found in channel "${i}"`};let v=async(r,b)=>{try{await this.notify(i,$,r,b),this.recordEvent(n)}catch(u){throw this.recordError(n),u}};try{return await Promise.race([g.listener.start(v),this.sleep(this.startTimeoutMs).then(()=>{throw Error(`listener start timed out after ${this.startTimeoutMs}ms`)})]),this.running.set(n,{config:g.config,channelName:i,channelId:g.channelId,listener:g.listener}),this.pendingRetry.delete(n),this.ensureStats(n),this.logger?.info(`${g.config.type} listener started`,{channel:i,connector:$}),{ok:!0}}catch(r){let b=r instanceof Error?r:Error(String(r));try{await g.listener.stop()}catch{}this.logger?.error(`${g.config.type} listener failed to start`,{channel:i,connector:$,error:b.message}),this.onError(b,{component:"listener-registry.start",channel:i,connector:$,type:g.config.type});let u=!(b instanceof $n);return{ok:!1,reason:b.message,retriable:u}}}async stop(i,$){let n=ti.keyOf(i,$),g=this.running.get(n);if(!g)return{ok:!0,reason:"not running"};try{return await g.listener.stop(),this.logger?.info(`${g.config.type} listener stopped`,{channel:i,connector:$}),{ok:!0}}catch(v){let r=v instanceof Error?v:Error(String(v));return this.logger?.error(`${g.config.type} listener failed to stop`,{channel:i,connector:$,error:r.message}),this.onError(r,{component:"listener-registry.stop",channel:i,connector:$,type:g.config.type}),{ok:!1,reason:r.message}}finally{this.running.delete(n),this.failureCounts.delete(n)}}async restart(i,$){let n=await this.stop(i,$);if(!n.ok)return n;return await this.start(i,$)}async startAll(){let i=this.channels.listAllConnectors(),$=await Promise.allSettled(i.map((n)=>this.start(n.channelName,n.name)));for(let n=0;n<$.length;n++){let g=$[n],v=i[n];if(g.status==="rejected"){let r=ti.keyOf(v.channelName,v.name);this.pendingRetry.set(r,{channelName:v.channelName,connectorName:v.name});continue}if(g.status==="fulfilled"&&!g.value.ok){if(g.value.retriable===!1)continue;let r=ti.keyOf(v.channelName,v.name);this.pendingRetry.set(r,{channelName:v.channelName,connectorName:v.name})}}this.startHealthCheck()}async stopAll(){this.stopHealthCheck(),this.pendingRetry.clear();for(let[,i]of this.running.entries())await this.stop(i.channelName,i.config.name)}ensureStats(i){let $=this.stats.get(i);if($)return $;let n={events:0,errors:0,failureCount:0,lastEventAt:null};return this.stats.set(i,n),n}recordEvent(i){let $=this.ensureStats(i);$.events+=1,$.lastEventAt=new Date(this.now()).toISOString()}recordError(i){this.ensureStats(i).errors+=1}startHealthCheck(){if(this.healthCheckTimer)return;this.healthCheckTimer=setInterval(()=>{this.runHealthCheck().catch((i)=>{let $=i instanceof Error?i:Error(String(i));this.logger?.error("health check pass failed",{error:$.message}),this.onError($,{component:"listener-registry.health-check"})})},this.healthCheckIntervalMs),this.healthCheckTimer.unref()}stopHealthCheck(){if(!this.healthCheckTimer)return;clearInterval(this.healthCheckTimer),this.healthCheckTimer=null}async runHealthCheckForTest(){await this.runHealthCheck()}async runHealthCheck(){if(this.healthCheckInFlight)return;this.healthCheckInFlight=!0;try{let i=[];for(let[n,g]of this.running.entries()){if(g.listener.isAlive()){this.failureCounts.delete(n);continue}i.push({channelName:g.channelName,connectorName:g.config.name,type:g.config.type})}await Promise.all(i.map((n)=>this.recoverDead(n.channelName,n.connectorName,n.type)));let $=[];for(let[n,g]of this.pendingRetry.entries()){if(this.running.has(n)){this.pendingRetry.delete(n);continue}$.push({key:n,channelName:g.channelName,connectorName:g.connectorName})}await Promise.all($.map((n)=>this.attemptRetry(n)))}finally{this.healthCheckInFlight=!1}}async attemptRetry(i){this.logger?.info("retrying failed listener",{channel:i.channelName,connector:i.connectorName});let $=this.failureCounts.get(i.key)??0,n=Math.min(1000*2**$,this.maxBackoffMs);await this.sleep(n);let g=await this.start(i.channelName,i.connectorName);if(g.ok){this.pendingRetry.delete(i.key),this.failureCounts.delete(i.key);return}if(g.retriable===!1){this.pendingRetry.delete(i.key),this.failureCounts.delete(i.key),this.logger?.warn("dropping listener from retry queue (non-retriable)",{channel:i.channelName,connector:i.connectorName,reason:g.reason});return}this.failureCounts.set(i.key,$+1)}async recoverDead(i,$,n){let g=ti.keyOf(i,$),v=this.failureCounts.get(g)??0,r=Math.min(1000*2**v,this.maxBackoffMs);this.logger?.warn(`${n} listener unhealthy, restarting`,{channel:i,connector:$,attempt:v+1,backoffMs:r}),await this.stop(i,$),await this.sleep(r);let b=await this.start(i,$);if(b.ok)this.failureCounts.delete(g),this.logger?.info(`${n} listener recovered`,{channel:i,connector:$});else if(b.retriable===!1)this.failureCounts.delete(g),this.logger?.warn(`${n} listener cannot recover (non-retriable)`,{channel:i,connector:$,reason:b.reason});else this.failureCounts.set(g,v+1)}}var MV=new R,xV=3000,NV=100,EV=200,tV=(i)=>new Promise(($)=>{setTimeout($,i)}),cV=(i)=>`funnel-gateway[${i}]`,CV=async(i)=>{let $=i.now()+xV;while(i.now()<$){if(i.pids.every((n)=>!i.runner.isAlive(n)))return;await i.sleep(NV)}for(let n of i.pids){if(!i.runner.isAlive(n))continue;try{i.runner.kill(n,"SIGKILL")}catch{}}await i.sleep(EV)},D2=async(i)=>{let $=i.process??MV,n=i.logger,g=cV(i.dir),v=$.listProcessesContaining(g),r=[];for(let b of v){if(b.pid===i.selfPid)continue;$.kill(b.pid,"SIGTERM"),r.push(b.pid),n?.info("killed competing Slack gateway process",{pid:b.pid,args:b.command.slice(0,160)})}if(r.length===0)return r;return await CV({runner:$,pids:r,sleep:i.sleep??tV,now:i.now??(()=>Date.now())}),r};var k2=(i,$,n)=>{let g=i.req.raw.headers.get("Cookie");if(typeof $==="string"){if(!g)return;let r=$;if(n==="secure")r="__Secure-"+$;else if(n==="host")r="__Host-"+$;return b_(g,r)[r]}if(!g)return{};return b_(g)};var RV=/^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i,FV=/^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/i,oV=/^application\/x-www-form-urlencoded(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i,p_=(i,$)=>{return async(n,g)=>{let v={},r=n.req.header("Content-Type");switch(i){case"json":if(!r||!RV.test(r))break;try{v=await n.req.json()}catch{throw new si(400,{message:"Malformed JSON in request body"})}break;case"form":{if(!r||!(FV.test(r)||oV.test(r)))break;let u;if(n.req.bodyCache.formData)u=await n.req.bodyCache.formData;else try{let I=await n.req.arrayBuffer();u=await Wr(I,r),n.req.bodyCache.formData=u}catch(I){let _="Malformed FormData request.";throw _+=I instanceof Error?` ${I.message}`:` ${String(I)}`,new si(400,{message:_})}let U=Object.create(null);u.forEach((I,_)=>{if(_.endsWith("[]"))(U[_]??=[]).push(I);else if(Array.isArray(U[_]))U[_].push(I);else if(Object.hasOwn(U,_))U[_]=[U[_],I];else U[_]=I}),v=U;break}case"query":v=Object.fromEntries(Object.entries(n.req.queries()).map(([u,U])=>{return U.length===1?[u,U[0]]:[u,U]}));break;case"param":v=n.req.param();break;case"header":v=n.req.header();break;case"cookie":v=k2(n);break}let b=await $(v,n);if(b instanceof Response)return b;return n.req.addValidatedData(i,b),await g()}};function mV(i,$,n,g){return p_(i,async(v,r)=>{let b=v;if(i==="header"&&"_def"in $||i==="header"&&"_zod"in $){let U=Object.keys("in"in $?$.in.shape:$.shape),I=Object.fromEntries(U.map((_)=>[_.toLowerCase(),_]));b=Object.fromEntries(Object.entries(v).map(([_,k])=>[I[_]||_,k]))}let u=g&&g.validationFunction?await g.validationFunction($,b):await $.safeParseAsync(b);if(n){let U=await n({data:b,...u,target:i},r);if(U){if(U instanceof Response)return U;if("response"in U)return U.response}}if(!u.success)return r.json(u,400);return u.data})}var Yr=mV;var Ki=(i)=>Yr("param",i,($,n)=>{if($.success)return;let g=$.error.issues[0],v=g?`${g.path.join(".")}: ${g.message}`:"invalid request";return n.json({ok:!1,reason:v},400)});var e_=D.lazy(()=>D.union([D.string(),D.number(),D.boolean(),D.null(),D.array(e_),D.record(D.string(),e_)])),yV=D.object({method:D.string().min(1),path:D.string().min(1),body:e_.optional()}),w2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=null;try{n=await i.req.json()}catch{n=null}let g=yV.safeParse(n);if(!g.success)throw new si(400,{message:g.error.issues[0]?.message??"invalid body"});let v=await i.var.deps.channels.call($.channel,$.connector,{method:g.data.method,path:g.data.path,body:g.data.body});return i.json({ok:!0,result:v})});var j2=c.createHandlers(Ki(D.object({channel:D.string().min(1)})),Yr("json",ck,(i,$)=>{if(i.success)return;let n=i.error.issues[0],g=n?`${n.path.join(".")}: ${n.message}`:"invalid body";return $.json({ok:!1,reason:g},400)}),(i)=>{let $=i.req.valid("param"),n=i.req.valid("json"),g=n.target?{...n.meta,target:n.target}:n.meta,r={ok:!0,offset:i.var.deps.emit({channel:$.channel,connector:n.connector,content:n.content,meta:g}).offset};return i.json(r)});var hV=(i)=>{let n=(i.connectionErrors[i.connectionErrors.length-1]??null)?.detail??null;if(i.connectors.length===0)return{status:"warn",message:"no connectors configured on this channel",nextActions:[`fnl channels ${i.name} connectors add <name> --type=slack ...`],rootCause:null};if(!i.listener)return{status:"error",message:"no listener running for this channel",nextActions:["fnl gateway restart"],rootCause:n};if(!i.listener.alive)return{status:"error",message:"listener is dead",nextActions:["fnl gateway logs","fnl gateway restart"],rootCause:n};if(i.claudeClients===0)return{status:"warn",message:"no Claude connected to this channel",nextActions:[`fnl claude --channel ${i.name}`],rootCause:null};if(i.listener.errors>0)return{status:"warn",message:"listener has errors",nextActions:["fnl gateway logs"],rootCause:n};return{status:"ok",message:"healthy",nextActions:[],rootCause:null}},O2=c.createHandlers(async(i)=>{let $=i.var.deps,n=i.req.query("channel")??null,g=$.channels.list(),v=n?g.filter((I)=>I.name===n||I.id===n):g,r=$.registry.list(),b=$.broadcaster.listChannels(),u=$.broadcaster.getMetrics(),U=v.map((I)=>{let _=r.find((B)=>B.channelName===I.name)??null,k=_?{alive:_.alive,events:_.events,errors:_.errors,lastEventAt:_.lastEventAt}:null,O=b.filter((B)=>B.channel===I.id||B.channel===I.name).length,J=(()=>{if(!$.diagnosticLog)return[];try{return $.diagnosticLog.queryProcessed({channelId:I.id,limit:10}).map(vv)}catch{return[]}})(),Q=k&&(!k.alive||k.errors>0)||!k,A=(()=>{if(!Q||!$.diagnosticLog)return[];try{return $.diagnosticLog.queryConnection({channelId:I.id,statuses:["auth-failed","error"],limit:3}).map(P$)}catch{return[]}})(),z={id:I.id,name:I.name,connectors:I.connectors.map((B)=>B.name),listener:k,claudeClients:O,recentEvents:J,connectionErrors:A};return{...z,diagnosis:hV(z)}});return i.json({pid:$.selfPid,uptimeMs:$.uptimeMs(),eventsBroadcast:u.eventsBroadcast,channels:U})});var J2=c.createHandlers((i)=>{let $=i.var.deps;return i.json({ok:!0,pid:$.selfPid,funnelDir:$.dir,clients:$.broadcaster.getClientCount(),listeners:$.registry.list()})});var P2=c.createHandlers((i)=>{return i.json({listeners:i.var.deps.registry.list()})});var W2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=await i.var.deps.registry.restart($.channel,$.connector);return i.json(n,n.ok?200:400)});var V2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=await i.var.deps.registry.start($.channel,$.connector);return i.json(n,n.ok?200:400)});var X2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=await i.var.deps.registry.stop($.channel,$.connector);return i.json(n,n.ok?200:400)});var f2=c.createHandlers((i)=>{let $=i.var.deps;return i.json({ok:!0,pid:$.selfPid,funnelDir:$.dir,uptimeMs:$.uptimeMs(),clients:$.broadcaster.listChannels(),listeners:$.registry.list(),broadcaster:$.broadcaster.getMetrics()})});function dV(){return c.createApp().onError((i,$)=>{if(i instanceof si)return i.getResponse();let n=i instanceof Error?i.message:String(i);return $.json({error:n},500)}).get("/health",...J2).get("/status",...f2).get("/debug",...O2).get("/listeners",...P2).post("/listeners/:channel/:connector/start",...V2).delete("/listeners/:channel/:connector",...X2).post("/listeners/:channel/:connector/restart",...W2).post("/channels/:channel/connectors/:connector/call",...w2).post("/channels/:channel/publish",...j2)}var Q2=dV();import{createHash as aV,randomUUID as pV}from"crypto";import{join as eV}from"path";function K2(i){let $=i.port===0?pV():String(i.port),n=aV("sha256").update(`${i.funnelDir}\x00${$}`).digest("hex").slice(0,20);return eV(i.tmpDir,"events",`${n}.db`)}var vX="127.0.0.1",nX=new Set(["127.0.0.1","localhost","::1","::ffff:127.0.0.1"]),gX=()=>{};class s_{channels;configuredPort;configuredHostname;dbPath;process;logger;onError;selfPid;dir;killCompetingSlack;token;allowInsecureHost;broadcaster;eventLog;registry;nowMs;extraRoutes;ownsEventLog;diagnosticLog;startedAt=null;server=null;disposed=!1;constructor(i){this.channels=i.channels,this.configuredPort=i.port??Mi(),this.configuredHostname=i.hostname??vX,this.dir=i.dir??ji,this.dbPath=i.dbPath??K2({tmpDir:i.tmpDir??fi(),funnelDir:this.dir,port:this.configuredPort}),this.process=i.process,this.logger=i.logger,this.onError=i.onError??gX,this.selfPid=i.selfPid??globalThis.process.pid,this.killCompetingSlack=i.killCompetingSlack??!0,this.token=i.token??"",this.allowInsecureHost=i.allowInsecureHost??!1,this.extraRoutes=i.extraRoutes??null,this.diagnosticLog=i.diagnosticLog;let $=i.clock;if(this.nowMs=$?()=>$.millis():()=>Date.now(),i.eventLog)this.eventLog=i.eventLog,this.ownsEventLog=!1;else{let n=$X(this.dbPath);if(!sV(n))iX(n,{recursive:!0});this.eventLog=new a_({path:this.dbPath,now:this.nowMs,logger:this.logger,onError:this.onError}),this.ownsEventLog=!0}this.broadcaster=new h_({logger:this.logger,onError:this.onError,now:this.nowMs,persistentReplay:this.eventLog}),this.broadcaster.seedLatestOffset(this.eventLog.findMaxOffset()),this.registry=new ti({channels:this.channels,logger:this.logger,onError:this.onError,notify:async(n,g,v,r)=>{this.emit({channel:n,connector:g,content:v,meta:r})},now:this.nowMs})}get port(){return this.server?.port??this.configuredPort}get hostname(){return this.server?.hostname??this.configuredHostname}async start(){if(this.disposed)throw Error("FunnelGatewayServer is single-use: construct a new instance to start again");if(this.server)return;if(!this.token&&!nX.has(this.configuredHostname)&&!this.allowInsecureHost)throw Error(`refusing to start gateway: hostname "${this.configuredHostname}" is reachable off-box but no token is set. Set a token, bind to loopback (127.0.0.1), or pass allowInsecureHost: true.`);let i=this.buildApp();await this.killCompetingSlackIfNeeded(),this.startedAt=this.nowMs(),this.server=Bun.serve({port:this.configuredPort,hostname:this.configuredHostname,development:!1,fetch:($,n)=>this.handleFetch($,n,i),websocket:{open:($)=>this.handleWsOpen($),close:($)=>this.handleWsClose($),message(){}}}),this.logServerStarted();try{await this.bootListeners()}catch($){throw this.server.stop(),this.server=null,$}}async stop(){if(await this.registry.stopAll(),this.server)this.server.stop(),this.server=null;if(this.ownsEventLog)this.eventLog.close();this.disposed=!0}getStatus(){return{clients:this.broadcaster.getClientCount(),channels:this.broadcaster.listChannels()}}getBroadcaster(){return this.broadcaster}getRegistry(){return this.registry}getEventLog(){return this.eventLog}onEvent(i){return this.broadcaster.subscribe(i)}handleFetch(i,$,n){let g=new URL(i.url);if(g.pathname==="/ws"&&i.headers.get("upgrade")==="websocket"){if(this.token&&!this.tokenMatchesUpgrade(i))return new Response("unauthorized",{status:401});let v=g.searchParams.get("channel")??"",r=v?this.resolveChannel(v):null;if(v&&!r)return new Response(`unknown channel "${v}"`,{status:404});let b=r?.id??v,u=r?.name??null,U=r?.connectors??[],I=r?.delivery??"fanout",_=g.searchParams.get("since"),k=_===null?Number.NaN:Number.parseInt(_,10),O=Number.isFinite(k)&&k>=0?k:void 0,J=g.searchParams.get("id")??void 0;if($.upgrade(i,{data:{channel:b,channelName:u,connectors:U,delivery:I,subscriberId:J,since:O}}))return;return new Response("WebSocket upgrade failed",{status:400})}return n.fetch(i)}handleWsOpen(i){if(typeof i.data.since==="number"){let $=this.broadcaster.replaySince(i.data.since,i.data);try{for(let n of $)i.send(JSON.stringify(n))}catch(n){let g=n instanceof Error?n:Error(String(n));this.logger?.warn("replay send failed during ws.open",{error:g.message}),this.onError(g,{component:"gateway-server.replay"});return}}this.broadcaster.addClient(i,i.data),this.logger?.info("channel connected",{event_type:"system",action:"channel_connect",channel:i.data.channelName??"",channelId:i.data.channel,connectors:i.data.connectors.join(","),total:String(this.broadcaster.getClientCount())})}handleWsClose(i){this.broadcaster.removeClient(i),this.logger?.info("channel disconnected",{event_type:"system",action:"channel_disconnect",channel:i.data.channelName??"",channelId:i.data.channel,total:String(this.broadcaster.getClientCount())})}logServerStarted(){this.logger?.info("gateway started",{event_type:"system",action:"gateway_start",port:String(this.port),pid:String(this.selfPid)}),this.logger?.info("funnel gateway listening",{url:`http://localhost:${this.port}`,websocket:`ws://localhost:${this.port}/ws`,health:`http://localhost:${this.port}/health`})}buildApp(){let i=c.createApp();if(i.use((n,g)=>{return n.set("deps",{selfPid:this.selfPid,dir:this.dir,broadcaster:this.broadcaster,registry:this.registry,channels:this.channels,uptimeMs:()=>this.startedAt?this.nowMs()-this.startedAt:0,emit:(v)=>this.emit(v),diagnosticLog:this.diagnosticLog}),g()}),this.token)i.use("/listeners/*",Ei({expected:this.token})),i.use("/status",Ei({expected:this.token})),i.use("/debug",Ei({expected:this.token})),i.use("/channels/*",Ei({expected:this.token}));return(this.extraRoutes?i.route("/",this.extraRoutes):i).route("/",Q2)}tokenMatchesUpgrade(i){let $=(i.headers.get("sec-websocket-protocol")??"").split(",").map((v)=>v.trim()).filter((v)=>v.length>0);for(let v of $)if(v.startsWith("funnel.token.")&&Pr(v.slice(13),this.token))return!0;let g=(i.headers.get("authorization")??"").match(/^Bearer\s+(.+)$/i);if(g&&Pr(g[1]??"",this.token))return!0;return!1}resolveChannel(i){let $=this.channels.get(i)??this.channels.getById(i);if(!$)return null;return{id:$.id,name:$.name,connectors:$.connectors.map((n)=>n.name),delivery:$.delivery}}async killCompetingSlackIfNeeded(){if(!this.killCompetingSlack)return;if(!this.channels.listAllConnectors().some((n)=>n.type==="slack"))return;let $=await D2({selfPid:this.selfPid,dir:this.dir,process:this.process,logger:this.logger});if($.length>0)this.logger?.info("killed competing Slack gateway processes",{event_type:"system",action:"kill_competing",pids:$.join(",")})}async bootListeners(){await this.registry.startAll();for(let i of this.registry.list())this.logger?.info(`${i.type} listener started: ${i.name}`,{event_type:"system",action:`${i.type}_connect`,channel:i.channelName,connector:i.name});this.logger?.info(`event store: ${this.dbPath}`),this.logger?.info("funnel gateway running")}emit(i){let $=this.lookupChannelId(i.channel),n=$&&i.connector?this.lookupConnectorId($,i.connector):null,g={...i.meta,channel:i.channel};if(i.connector)g.connector=i.connector;if($)g.channelId=$;if(n)g.connectorId=n;let v=this.broadcaster.broadcast(i.content,g);return this.eventLog.record({content:i.content,channelId:$??null,connectorId:n??null,meta:g,offset:v.offset}),{offset:v.offset}}lookupChannelId(i){return this.channels.get(i)?.id??this.channels.getById(i)?.id??null}lookupConnectorId(i,$){return this.channels.getById(i)?.connectors.find((g)=>g.name===$)?.id??null}}import{homedir as rX}from"os";import{dirname as bX,join as G2}from"path";var Y2="gateway.token",uX=32,UX=new F,IX=()=>{let i=new Uint8Array(uX);return crypto.getRandomValues(i),[...i].map(($)=>$.toString(16).padStart(2,"0")).join("")};class i1{fs;path;generate;constructor(i={}){this.fs=i.fs??UX,this.path=G2(i.dir??ji,Y2),this.generate=i.generate??IX,Object.freeze(this)}read(){if(!this.fs.existsSync(this.path))return null;let i=this.fs.readFileSync(this.path).trim();return i.length>0?i:null}ensure(){return this.fs.mkdirSync(bX(this.path),{recursive:!0}),this.fs.withFileLock(`${this.path}.lock`,()=>{let i=this.read();if(i)return i;let $=this.generate();return this.fs.writeSecretFileSync(this.path,`${$}
795
+ `,MW=String.fromCharCode(8),xW=String.fromCharCode(127),NW=String.fromCharCode(3),EW=String.fromCharCode(4);class z_ extends Un{async promptSecret(i){if(!Qi.isTTY)throw Error(`cannot prompt for "${i}": stdin is not a TTY. Set the matching env var or run \`fnl channels <ch> connectors add ...\` first.`);Or.write(`${i}: `);let $=Qi.isRaw;Qi.setRawMode(!0),Qi.resume();try{return await this.readSecret()}finally{Qi.setRawMode($),Qi.pause(),Or.write(Nk)}}readSecret(){return new Promise((i,$)=>{let n="",g=(v)=>{for(let r of v){let b=String.fromCharCode(r);if(b===Nk||b===qW){Qi.off("data",g),i(n);return}if(b===NW){Qi.off("data",g),$(Error("prompt cancelled"));return}if(b===EW){if(Qi.off("data",g),n.length===0)$(Error("prompt cancelled"));else i(n);return}if(b===MW||b===xW){if(n.length>0)n=n.slice(0,-1),Or.write("\b \b");continue}n+=b,Or.write(ZW)}};Qi.on("data",g)})}}var tW=384;class S_ extends av{dirs;files;mtimes;modes;now;constructor(i={}){super();this.dirs=new Set(i.dirs??[]),this.files=new Map(Object.entries(i.files??{})),this.mtimes=new Map(Object.entries(i.mtimes??{})),this.modes=new Map(Object.entries(i.modes??{})),this.now=i.now??(()=>Date.now())}existsSync(i){return this.dirs.has(i)||this.files.has(i)}readFileSync(i){return this.files.get(i)??""}writeFileSync(i,$){this.files.set(i,$),this.touch(i)}writeSecretFileSync(i,$){this.files.set(i,$),this.modes.set(i,tW),this.touch(i)}appendFileSync(i,$){let n=this.files.get(i)??"";this.files.set(i,n+$),this.touch(i)}unlink(i){this.files.delete(i),this.mtimes.delete(i),this.modes.delete(i)}mkdirSync(i,$){this.dirs.add(i)}readdirSync(i){let $=i.endsWith("/")?i:`${i}/`,n=[];for(let g of this.files.keys()){if(!g.startsWith($))continue;let v=g.slice($.length);if(!v.includes("/"))n.push(v)}return n}statSync(i){let $=this.mtimes.get(i);if($===void 0)throw Error(`not found: ${i}`);return{mtimeMs:$,mode:this.modes.get(i)??null}}withFileLock(i,$){return $()}setMtime(i,$){this.mtimes.set(i,$)}setMode(i,$){this.modes.set(i,$)}touch(i){if(!this.mtimes.has(i))this.mtimes.set(i,this.now());else this.mtimes.set(i,this.now())}}class A_ extends vn{counter=0;prefix;constructor(i={}){super();this.prefix=i.prefix??"id"}generate(){return this.counter++,`${this.prefix}-${this.counter}`}}class In{}class B_ extends In{file=null;entries=[];info(i,$){this.entries.push({level:"info",message:i,meta:$})}warn(i,$){this.entries.push({level:"warn",message:i,meta:$})}error(i,$){this.entries.push({level:"error",message:i,meta:$})}clear(){this.entries.length=0}}var Ek={exitCode:0,stdout:"",stderr:""};class l_ extends yv{calls=[];killed=[];handler=()=>Ek;syncHandler=()=>Ek;aliveStub=null;listStub=null;startTimeStub=null;on(i){return this.handler=i,this}onSync(i){return this.syncHandler=i,this}onIsAlive(i){return this.aliveStub=i,this}onListProcessesContaining(i){return this.listStub=i,this}onGetStartTime(i){return this.startTimeStub=i,this}async run(i,$={}){this.calls.push({kind:"run",command:i,options:$});let n=await this.handler(i);return{exitCode:n.exitCode??0,stdout:n.stdout??"",stderr:n.stderr??""}}runSync(i){this.calls.push({kind:"runSync",command:i});let $=this.syncHandler(i);return{exitCode:$.exitCode??0,stdout:$.stdout??"",stderr:$.stderr??""}}async attach(i,$={}){if(this.calls.push({kind:"attach",command:i,options:$}),$.onSpawned)$.onSpawned(1);return(await this.handler(i)).exitCode??0}detach(i,$={}){this.calls.push({kind:"detach",command:i,options:$})}kill(i,$="SIGTERM"){this.calls.push({kind:"kill",command:[String(i),$]}),this.killed.push({pid:i,signal:$})}isAlive(i){if(this.aliveStub)return this.aliveStub(i);let $=this.syncHandler(["ps","-p",String(i),"-o","state="]);if(($.exitCode??0)!==0)return!1;let n=($.stdout??"").trim();if(!n)return!1;return!n.startsWith("Z")}listProcessesContaining(i){if(this.listStub)return this.listStub(i);return[]}getStartTime(i){if(this.startTimeStub)return this.startTimeStub(i);return null}}var cW=(i={})=>({version:qi,channels:[],profiles:[],...i});class L_ extends nn{state;constructor(i){super();this.state=cW(i)}read(){return this.state}write(i){this.state=i}update(i){return i(this.state)}}class T_ extends rn{current;constructor(i={}){super();this.current=i.start??new Date(0)}now(){return new Date(this.current.getTime())}set(i){this.current=i}advance(i){this.current=new Date(this.current.getTime()+i)}}class Z_ extends Uv{calls=[];handler=()=>({status:200,body:""});on(i){return this.handler=i,this}async fetch(i){this.calls.push(i);let $=await this.handler(i),n=$.status??200,g=$.body??"";return{status:n,ok:n>=200&&n<300,text:async()=>g,json:async()=>JSON.parse(g)}}}class q_ extends Un{answers;asked=[];constructor(i={}){super();this.answers=new Map(Object.entries(i.answers??{}))}async promptSecret(i){this.asked.push(i);let $=this.answers.get(i);if($===void 0)throw Error(`no answer seeded for prompt "${i}"`);return $}}var CW=["started","connected","disconnected","auth-failed","stopped","error"],A7=D.object({event_id:D.string(),type:D.string(),connector_id:D.string().nullable(),channel_id:D.string().nullable(),payload:D.string()}),B7=D.object({event_id:D.string(),type:D.string(),connector_id:D.string().nullable(),channel_id:D.string().nullable(),outcome:D.string(),payload:D.string()}),tk=D.object({type:D.string(),connector_id:D.string().nullable(),channel_id:D.string().nullable(),status:D.enum(CW),detail:D.string()});class _n{}class N_ extends _n{now;raws=[];processeds=[];connections=[];constructor(i=()=>Date.now()){super();this.now=i;Object.freeze(this)}recordRaw(i){this.raws.push({...i,seq:this.raws.length+1,ts:this.now()})}recordProcessed(i){this.processeds.push({...i,seq:this.processeds.length+1,ts:this.now()})}recordConnection(i){this.connections.push({...i,seq:this.connections.length+1,ts:this.now()})}queryRaw(i){let $=this.raws.filter((n)=>{if(!M_(n,i))return!1;if(i.eventId!==void 0&&n.eventId!==i.eventId)return!1;return!0});return x_($,i.limit)}queryProcessed(i){let $=this.processeds.filter((n)=>{if(!M_(n,i))return!1;if(i.eventId!==void 0&&n.eventId!==i.eventId)return!1;if(i.outcome!==void 0&&n.outcome!==i.outcome)return!1;if(i.outcomePrefix!==void 0&&!n.outcome.startsWith(i.outcomePrefix))return!1;return!0});return x_($,i.limit)}queryConnection(i){let $=this.connections.filter((n)=>{if(!M_(n,i))return!1;if(i.status!==void 0&&n.status!==i.status)return!1;if(i.statuses!==void 0&&!i.statuses.includes(n.status))return!1;return!0});return x_($,i.limit)}clear(){this.raws.length=0,this.processeds.length=0,this.connections.length=0}close(){}}var M_=(i,$)=>{if($.type!==void 0&&i.type!==$.type)return!1;if($.connectorId!==void 0&&i.connectorId!==$.connectorId)return!1;if($.channelId!==void 0&&i.channelId!==$.channelId)return!1;if("seq"in i&&$.seq!==void 0&&i.seq!==$.seq)return!1;return!0},x_=(i,$)=>{if($===void 0)return i;if($<=0)return[];return i.slice(-$)};var ck=D.object({content:D.string().min(1),meta:D.record(D.string(),D.string()).optional(),connector:D.string().min(1).optional(),target:D.string().min(1).optional()}),Ck=D.object({ok:D.literal(!0),offset:D.number().int().nonnegative()});var RW={state:"offline"};class E_{port;isDaemonRunning;getToken;constructor(i){this.port=i.port,this.isDaemonRunning=i.isDaemonRunning,this.getToken=i.getToken??(()=>null),Object.freeze(this)}async publish(i,$){if(!this.isDaemonRunning())return RW;try{let n=`${xi(this.port)}/channels/${encodeURIComponent(i)}/publish`,g=await J$(n,{method:"POST",headers:{...this.authHeaders(),"content-type":"application/json"},body:JSON.stringify($)});if(!g.ok)return{state:"error",reason:await g.text()||`HTTP ${g.status}`};let v=Ck.safeParse(await g.json());if(!v.success)return{state:"error",reason:"malformed daemon response"};return{state:"ok",offset:v.data.offset}}catch(n){return{state:"error",reason:n instanceof Error?n.message:String(n)}}}authHeaders(){let i=this.getToken();return i?{authorization:`Bearer ${i}`}:{}}}import{join as Rk}from"path";import{existsSync as FW}from"fs";import{dirname as oW,resolve as t_}from"path";import{fileURLToPath as mW}from"url";var Jr=()=>{let i=oW(mW(import.meta.url)),$=[t_(i,"./daemon.ts"),t_(i,"./daemon.js"),t_(i,"./gateway/daemon.js")];for(let n of $)if(FW(n))return n;throw Error(`daemon script not found (looked in ${$.join(", ")})`)};var yW=5000,hW=2000,Fk=100,dW=200,aW=new R,pW=new F,eW=new Ni,sW=(i)=>new Promise(($)=>{setTimeout($,i)});class c_{process;fs;clock;dir;pidFile;gatewayLog;tmpDir;port;sleep;constructor(i={}){this.process=i.process??aW,this.fs=i.fs??pW,this.clock=i.clock??eW,this.dir=i.dir??ji,this.tmpDir=i.tmpDir??fi(),this.pidFile=Rk(this.dir,"gateway.pid"),this.gatewayLog=Rk(this.tmpDir,"gateway.log"),this.port=i.port??Mi(),this.sleep=i.sleep??sW,Object.freeze(this)}isRunning(){let i=this.readPid();if(!i)return!1;return this.isProcessAlive(i)}getStatus(){let i=this.readPid(),$=i!==null&&this.isProcessAlive(i);return{running:$,pid:$?i:null,port:this.port}}async start(i={}){if(this.isRunning())return!0;this.fs.mkdirSync(this.tmpDir,{recursive:!0});let $=Jr(),n=this.buildStartCommand($,i);this.process.detach(n,{env:{FUNNEL_DIR:this.dir,FUNNEL_PORT:String(this.port),FUNNEL_TMP_DIR:this.tmpDir},stdoutFile:this.gatewayLog,stderrFile:this.gatewayLog});let g=this.clock.millis()+yW;while(this.clock.millis()<g){if(this.isRunning())return!0;await this.sleep(Fk)}return this.isRunning()}buildStartCommand(i,$={}){let n=`funnel-gateway[${this.dir}]`;if($.caffeinate!==!1&&globalThis.process.platform==="darwin")return["caffeinate","-is","bun",i,n];return["bun",i,n]}async stop(){let i=this.readPid();if(!i)return!0;if(!this.isProcessAlive(i))return this.removePid(),!0;try{this.process.kill(i,"SIGTERM")}catch{return!1}let $=this.clock.millis()+hW;while(this.clock.millis()<$){if(!this.isProcessAlive(i))return this.removePid(),!0;await this.sleep(Fk)}try{this.process.kill(i,"SIGKILL")}catch{}return await this.sleep(dW),this.removePid(),!this.isProcessAlive(i)}async restart(i={}){let $=this.isRunning();if(i.onlyIfRunning&&!$)return{ok:!0,wasRunning:!1,stopped:!1,started:!1};let n=$?await this.stop():!0;if(!n)return{ok:!1,wasRunning:$,stopped:!1,started:!1};let g=await this.start({caffeinate:i.caffeinate});return{ok:g,wasRunning:$,stopped:n,started:g}}getGatewayLog(){return this.gatewayLog}getPort(){return this.port}readPid(){if(!this.fs.existsSync(this.pidFile))return null;try{let i=this.fs.readFileSync(this.pidFile).trim(),$=Number(i);if(!$||$<=0)return null;return $}catch{return null}}removePid(){this.fs.unlink(this.pidFile)}isProcessAlive(i){return this.process.isAlive(i)}}import{existsSync as sV,mkdirSync as iX}from"fs";import{dirname as $X}from"path";import{timingSafeEqual as iV}from"crypto";var Ei=(i)=>{return async($,n)=>{let r=($.req.header("authorization")??"").match(/^Bearer\s+(.+)$/i)?.[1]??"";if(!Pr(r,i.expected))return $.text("unauthorized",401);return await n()}},Pr=(i,$)=>{let n=Buffer.from(i,"utf-8"),g=Buffer.from($,"utf-8"),v=Math.max(n.length,g.length,1),r=Buffer.alloc(v),b=Buffer.alloc(v);return n.copy(r),g.copy(b),iV(r,b)&&n.length===g.length};var C_=(i,$,n)=>{return(g,v)=>{let r=-1;return b(0);async function b(u){if(u<=r)throw Error("next() called multiple times");r=u;let U,I=!1,_;if(i[u])_=i[u][0][0],g.req.routeIndex=u;else _=u===i.length&&v||void 0;if(_)try{U=await _(g,()=>b(u+1))}catch(k){if(k instanceof Error&&$)g.error=k,U=await $(k,g),I=!0;else throw k}else if(g.finalized===!1&&n)U=await n(g);if(U&&(g.finalized===!1||I))g.res=U;return g}}};var si=class extends Error{res;status;constructor(i=500,$){super($?.message,{cause:$?.cause});this.res=$?.res,this.status=i}getResponse(){if(this.res)return new Response(this.res.body,{status:this.status,headers:this.res.headers});return new Response(this.message,{status:this.status})}};var ok=Symbol();var Wr=(i,$)=>{return new Response(i,{headers:{"Content-Type":$.replace(/^[^;]+/,(g)=>g.toLowerCase())}}).formData()};var Vr=(i)=>("headers"in i),yk=async(i,$=Object.create(null))=>{let{all:n=!1,dot:g=!1}=$,b=(Vr(i)?i.headers:i.raw.headers).get("Content-Type")?.split(";")[0].trim().toLowerCase();if(b==="multipart/form-data"||b==="application/x-www-form-urlencoded")return $V(i,{all:n,dot:g});return{}};async function $V(i,$){if(!Vr(i)&&i.bodyCache.formData)return mk(await i.bodyCache.formData,$);let n=Vr(i)?i.headers:i.raw.headers,g=await i.arrayBuffer(),v=Wr(g,n.get("Content-Type")||"");if(!Vr(i))i.bodyCache.formData=v;let r=await v;if(r)return mk(r,$);return{}}function mk(i,$){let n=Object.create(null);if(i.forEach((g,v)=>{if(!($.all||v.endsWith("[]")))n[v]=g;else vV(n,v,g)}),$.dot)Object.entries(n).forEach(([g,v])=>{if(g.includes("."))nV(n,g,v),delete n[g]});return n}var vV=(i,$,n)=>{if(i[$]!==void 0)if(Array.isArray(i[$]))i[$].push(n);else i[$]=[i[$],n];else if(!$.endsWith("[]"))i[$]=n;else i[$]=[n]},nV=(i,$,n)=>{if(/(?:^|\.)__proto__\./.test($))return;let g=i,v=$.split(".");v.forEach((r,b)=>{if(b===v.length-1)g[r]=n;else{if(!g[r]||typeof g[r]!=="object"||Array.isArray(g[r])||g[r]instanceof File)g[r]=Object.create(null);g=g[r]}})};var hk=(i)=>$v(i,gn),dk=class{raw;#i;#$;routeIndex=0;path;bodyCache={};constructor(i,$="/",n=[[]]){this.raw=i,this.path=$,this.#$=n,this.#i={}}param(i){return i?this.#v(i):this.#r()}#v(i){let $=this.#$[0][this.routeIndex][1][i],n=this.#g($);return n&&/\%/.test(n)?hk(n):n}#r(){let i={},$=Object.keys(this.#$[0][this.routeIndex][1]);for(let n of $){let g=this.#g(this.#$[0][this.routeIndex][1][n]);if(g!==void 0)i[n]=/\%/.test(g)?hk(g):g}return i}#g(i){return this.#$[1]?this.#$[1][i]:i}query(i){return uk(this.url,i)}queries(i){return Uk(this.url,i)}header(i){if(i)return this.raw.headers.get(i)??void 0;let $=Object.create(null);return this.raw.headers.forEach((n,g)=>{$[g]=n}),$}async parseBody(i){return yk(this,i)}#n=(i)=>{let{bodyCache:$,raw:n}=this,g=$[i];if(g)return g;let v=Object.keys($)[0];if(v)return $[v].then((r)=>{if(v==="json")r=JSON.stringify(r);return new Response(r)[i]()});return $[i]=n[i]()};json(){return this.#n("text").then((i)=>JSON.parse(i))}text(){return this.#n("text")}arrayBuffer(){return this.#n("arrayBuffer")}bytes(){return this.#n("arrayBuffer").then((i)=>new Uint8Array(i))}blob(){return this.#n("blob")}formData(){return this.#n("formData")}addValidatedData(i,$){this.#i[i]=$}valid(i){return this.#i[i]}get url(){return this.raw.url}get method(){return this.raw.method}get[ok](){return this.#$}get matchedRoutes(){return this.#$[0].map(([[,i]])=>i)}get routePath(){return this.#$[0].map(([[,i]])=>i)[this.routeIndex].path}};var ak={Stringify:1,BeforeStream:2,Stream:3},gV=(i,$)=>{let n=new String(i);return n.isEscaped=!0,n.callbacks=$,n};var R_=async(i,$,n,g,v)=>{if(typeof i==="object"&&!(i instanceof String)){if(!(i instanceof Promise))i=i.toString();if(i instanceof Promise)i=await i}let r=i.callbacks;if(!r?.length)return Promise.resolve(i);if(v)v[0]+=i;else v=[i];let b=Promise.all(r.map((u)=>u({phase:$,buffer:v,context:g}))).then((u)=>Promise.all(u.filter(Boolean).map((U)=>R_(U,$,!1,g,v))).then(()=>v[0]));if(n)return gV(await b,r);else return b};var rV="text/plain; charset=UTF-8",F_=(i,$)=>{return{"Content-Type":i,...$}},Dn=(i,$)=>new Response(i,$),o_=class{#i;#$;env={};#v;finalized=!1;error;#r;#g;#n;#_;#U;#I;#u;#D;#k;constructor(i,$){if(this.#i=i,$)this.#g=$.executionCtx,this.env=$.env,this.#I=$.notFoundHandler,this.#k=$.path,this.#D=$.matchResult}get req(){return this.#$??=new dk(this.#i,this.#k,this.#D),this.#$}get event(){if(this.#g&&"respondWith"in this.#g)return this.#g;else throw Error("This context has no FetchEvent")}get executionCtx(){if(this.#g)return this.#g;else throw Error("This context has no ExecutionContext")}get res(){return this.#n||=Dn(null,{headers:this.#u??=new Headers})}set res(i){if(this.#n&&i){i=Dn(i.body,i);for(let[$,n]of this.#n.headers.entries()){if($==="content-type")continue;if($==="set-cookie"){let g=this.#n.headers.getSetCookie();i.headers.delete("set-cookie");for(let v of g)i.headers.append("set-cookie",v)}else i.headers.set($,n)}}this.#n=i,this.finalized=!0}render=(...i)=>{return this.#U??=($)=>this.html($),this.#U(...i)};setLayout=(i)=>this.#_=i;getLayout=()=>this.#_;setRenderer=(i)=>{this.#U=i};header=(i,$,n)=>{if(this.finalized)this.#n=Dn(this.#n.body,this.#n);let g=this.#n?this.#n.headers:this.#u??=new Headers;if($===void 0)g.delete(i);else if(n?.append)g.append(i,$);else g.set(i,$)};status=(i)=>{this.#r=i};set=(i,$)=>{this.#v??=new Map,this.#v.set(i,$)};get=(i)=>{return this.#v?this.#v.get(i):void 0};get var(){if(!this.#v)return{};return Object.fromEntries(this.#v)}#b(i,$,n){let g=this.#n?new Headers(this.#n.headers):this.#u??new Headers;if(typeof $==="object"&&"headers"in $){let r=$.headers instanceof Headers?$.headers:new Headers($.headers);for(let[b,u]of r)if(b.toLowerCase()==="set-cookie")g.append(b,u);else g.set(b,u)}if(n)for(let[r,b]of Object.entries(n))if(typeof b==="string")g.set(r,b);else{g.delete(r);for(let u of b)g.append(r,u)}let v=typeof $==="number"?$:$?.status??this.#r;return Dn(i,{status:v,headers:g})}newResponse=(...i)=>this.#b(...i);body=(i,$,n)=>this.#b(i,$,n);text=(i,$,n)=>{return!this.#u&&!this.#r&&!$&&!n&&!this.finalized?new Response(i):this.#b(i,$,F_(rV,n))};json=(i,$,n)=>{return this.#b(JSON.stringify(i),$,F_("application/json",n))};html=(i,$,n)=>{let g=(v)=>this.#b(v,$,F_("text/html; charset=UTF-8",n));return typeof i==="object"?R_(i,ak.Stringify,!1,{}).then(g):g(i)};redirect=(i,$)=>{let n=String(i);return this.header("Location",!/[^\x00-\xFF]/.test(n)?n:encodeURI(n)),this.newResponse(null,$??302)};notFound=()=>{return this.#I??=()=>Dn(),this.#I(this)}};var x="ALL",pk="all",ek=["get","post","put","delete","options","patch"],Xr="Can not add a route since the matcher is already built.",fr=class extends Error{};var sk="__COMPOSED_HANDLER";var bV=(i)=>{return i.text("404 Not Found",404)},i2=(i,$)=>{if("getResponse"in i){let n=i.getResponse();return $.newResponse(n.body,n)}return console.error(i),$.text("Internal Server Error",500)},$2=class i{get;post;put;delete;options;patch;all;on;use;router;getPath;_basePath="/";#i="/";routes=[];constructor($={}){[...ek,pk].forEach((r)=>{this[r]=(b,...u)=>{if(typeof b==="string")this.#i=b;else this.#r(r,this.#i,b);return u.forEach((U)=>{this.#r(r,this.#i,U)}),this}}),this.on=(r,b,...u)=>{for(let U of[b].flat()){this.#i=U;for(let I of[r].flat())u.map((_)=>{this.#r(I.toUpperCase(),this.#i,_)})}return this},this.use=(r,...b)=>{if(typeof r==="string")this.#i=r;else this.#i="*",b.unshift(r);return b.forEach((u)=>{this.#r(x,this.#i,u)}),this};let{strict:g,...v}=$;Object.assign(this,v),this.getPath=g??!0?$.getPath??r_:rk}#$(){let $=new i({router:this.router,getPath:this.getPath});return $.errorHandler=this.errorHandler,$.#v=this.#v,$.routes=this.routes,$}#v=bV;errorHandler=i2;route($,n){let g=this.basePath($);return n.routes.map((v)=>{let r;if(n.errorHandler===i2)r=v.handler;else r=async(b,u)=>(await C_([],n.errorHandler)(b,()=>v.handler(b,u))).res,r[sk]=v.handler;g.#r(v.method,v.path,r,v.basePath)}),this}basePath($){let n=this.#$();return n._basePath=ei(this._basePath,$),n}onError=($)=>{return this.errorHandler=$,this};notFound=($)=>{return this.#v=$,this};mount($,n,g){let v,r;if(g)if(typeof g==="function")r=g;else if(r=g.optionHandler,g.replaceRequest===!1)v=(U)=>U;else v=g.replaceRequest;let b=r?(U)=>{let I=r(U);return Array.isArray(I)?I:[I]}:(U)=>{let I=void 0;try{I=U.executionCtx}catch{}return[U.env,I]};v||=(()=>{let U=ei(this._basePath,$),I=U==="/"?0:U.length;return(_)=>{let k=new URL(_.url);return k.pathname=this.getPath(_).slice(I)||"/",new Request(k,_)}})();let u=async(U,I)=>{let _=await n(v(U.req.raw),...b(U));if(_)return _;await I()};return this.#r(x,ei($,"*"),u),this}#r($,n,g,v){$=$.toUpperCase(),n=ei(this._basePath,n);let r={basePath:v!==void 0?ei(this._basePath,v):this._basePath,path:n,method:$,handler:g};this.router.add($,n,[g,r]),this.routes.push(r)}#g($,n){if($ instanceof Error)return this.errorHandler($,n);throw $}#n($,n,g,v){if(v==="HEAD")return(async()=>new Response(null,await this.#n($,n,g,"GET")))();let r=this.getPath($,{env:g}),b=this.router.match(v,r),u=new o_($,{path:r,matchResult:b,env:g,executionCtx:n,notFoundHandler:this.#v});if(b[0].length===1){let I;try{I=b[0][0][0][0](u,async()=>{u.res=await this.#v(u)})}catch(_){return this.#g(_,u)}return I instanceof Promise?I.then((_)=>_||(u.finalized?u.res:this.#v(u))).catch((_)=>this.#g(_,u)):I??this.#v(u)}let U=C_(b[0],this.errorHandler,this.#v);return(async()=>{try{let I=await U(u);if(!I.finalized)throw Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");return I.res}catch(I){return this.#g(I,u)}})()}fetch=($,...n)=>{return this.#n($,n[1],n[0],$.method)};request=($,n,g,v)=>{if($ instanceof Request)return this.fetch(n?new Request($,n):$,g,v);return $=$.toString(),this.fetch(new Request(/^https?:\/\//.test($)?$:`http://localhost${ei("/",$)}`,n),g,v)};fire=()=>{addEventListener("fetch",($)=>{$.respondWith(this.#n($.request,$,void 0,$.request.method))})}};var kn=[];function Qr(i,$){let n=this.buildAllMatchers(),g=(v,r)=>{let b=n[v]||n[x],u=b[2][r];if(u)return u;let U=r.match(b[0]);if(!U)return[[],kn];let I=U.indexOf("",1);return[b[1][I],U]};return this.match=g,g(i,$)}var Kr="[^/]+",wn=".*",jn="(?:|/.*)",W$=Symbol(),uV=new Set(".\\+*[^]$()");function UV(i,$){if(i.length===1)return $.length===1?i<$?-1:1:-1;if($.length===1)return 1;if(i===wn||i===jn)return 1;else if($===wn||$===jn)return-1;if(i===Kr)return 1;else if($===Kr)return-1;return i.length===$.length?i<$?-1:1:$.length-i.length}var v2=class i{#i;#$;#v=Object.create(null);insert($,n,g,v,r){if($.length===0){if(this.#i!==void 0)throw W$;if(r)return;this.#i=n;return}let[b,...u]=$,U=b==="*"?u.length===0?["","",wn]:["","",Kr]:b==="/*"?["","",jn]:b.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/),I;if(U){let _=U[1],k=U[2]||Kr;if(_&&U[2]){if(k===".*")throw W$;if(k=k.replace(/^\((?!\?:)(?=[^)]+\)$)/,"(?:"),/\((?!\?:)/.test(k))throw W$}if(I=this.#v[k],!I){if(Object.keys(this.#v).some((O)=>O!==wn&&O!==jn))throw W$;if(r)return;if(I=this.#v[k]=new i,_!=="")I.#$=v.varIndex++}if(!r&&_!=="")g.push([_,I.#$])}else if(I=this.#v[b],!I){if(Object.keys(this.#v).some((_)=>_.length>1&&_!==wn&&_!==jn))throw W$;if(r)return;I=this.#v[b]=new i}I.insert(u,n,g,v,r)}buildRegExpStr(){let n=Object.keys(this.#v).sort(UV).map((g)=>{let v=this.#v[g];return(typeof v.#$==="number"?`(${g})@${v.#$}`:uV.has(g)?`\\${g}`:g)+v.buildRegExpStr()});if(typeof this.#i==="number")n.unshift(`#${this.#i}`);if(n.length===0)return"";if(n.length===1)return n[0];return"(?:"+n.join("|")+")"}};var n2=class{#i={varIndex:0};#$=new v2;insert(i,$,n){let g=[],v=[];for(let b=0;;){let u=!1;if(i=i.replace(/\{[^}]+\}/g,(U)=>{let I=`@\\${b}`;return v[b]=[I,U],b++,u=!0,I}),!u)break}let r=i.match(/(?::[^\/]+)|(?:\/\*$)|./g)||[];for(let b=v.length-1;b>=0;b--){let[u]=v[b];for(let U=r.length-1;U>=0;U--)if(r[U].indexOf(u)!==-1){r[U]=r[U].replace(u,v[b][1]);break}}return this.#$.insert(r,$,g,this.#i,n),g}buildRegExp(){let i=this.#$.buildRegExpStr();if(i==="")return[/^$/,[],[]];let $=0,n=[],g=[];return i=i.replace(/#(\d+)|@(\d+)|\.\*\$/g,(v,r,b)=>{if(r!==void 0)return n[++$]=Number(r),"$()";if(b!==void 0)return g[Number(b)]=++$,"";return""}),[new RegExp(`^${i}`),n,g]}};var IV=[/^$/,[],Object.create(null)],g2=Object.create(null);function r2(i){return g2[i]??=new RegExp(i==="*"?"":`^${i.replace(/\/\*$|([.\\+*[^\]$()])/g,($,n)=>n?`\\${n}`:"(?:|/.*)")}$`)}function _V(){g2=Object.create(null)}function DV(i){let $=new n2,n=[];if(i.length===0)return IV;let g=i.map((I)=>[!/\*|\/:/.test(I[0]),...I]).sort(([I,_],[k,O])=>I?1:k?-1:_.length-O.length),v=Object.create(null);for(let I=0,_=-1,k=g.length;I<k;I++){let[O,J,Q]=g[I];if(O)v[J]=[Q.map(([z])=>[z,Object.create(null)]),kn];else _++;let A;try{A=$.insert(J,_,O)}catch(z){throw z===W$?new fr(J):z}if(O)continue;n[_]=Q.map(([z,B])=>{let Ii=Object.create(null);B-=1;for(;B>=0;B--){let[y,N]=A[B];Ii[y]=N}return[z,Ii]})}let[r,b,u]=$.buildRegExp();for(let I=0,_=n.length;I<_;I++)for(let k=0,O=n[I].length;k<O;k++){let J=n[I][k]?.[1];if(!J)continue;let Q=Object.keys(J);for(let A=0,z=Q.length;A<z;A++)J[Q[A]]=u[J[Q[A]]]}let U=[];for(let I in b)U[I]=n[b[I]];return[r,U,v]}function gv(i,$){if(!i)return;for(let n of Object.keys(i).sort((g,v)=>v.length-g.length))if(r2(n).test($))return[...i[n]];return}var Gr=class{name="RegExpRouter";#i;#$;constructor(){this.#i={[x]:Object.create(null)},this.#$={[x]:Object.create(null)}}add(i,$,n){let g=this.#i,v=this.#$;if(!g||!v)throw Error(Xr);if(!g[i])[g,v].forEach((u)=>{u[i]=Object.create(null),Object.keys(u[x]).forEach((U)=>{u[i][U]=[...u[x][U]]})});if($==="/*")$="*";let r=($.match(/\/:/g)||[]).length;if(/\*$/.test($)){let u=r2($);if(i===x)Object.keys(g).forEach((U)=>{g[U][$]||=gv(g[U],$)||gv(g[x],$)||[]});else g[i][$]||=gv(g[i],$)||gv(g[x],$)||[];Object.keys(g).forEach((U)=>{if(i===x||i===U)Object.keys(g[U]).forEach((I)=>{u.test(I)&&g[U][I].push([n,r])})}),Object.keys(v).forEach((U)=>{if(i===x||i===U)Object.keys(v[U]).forEach((I)=>u.test(I)&&v[U][I].push([n,r]))});return}let b=Ir($)||[$];for(let u=0,U=b.length;u<U;u++){let I=b[u];Object.keys(v).forEach((_)=>{if(i===x||i===_)v[_][I]||=[...gv(g[_],I)||gv(g[x],I)||[]],v[_][I].push([n,r-U+u+1])})}}match=Qr;buildAllMatchers(){let i=Object.create(null);return Object.keys(this.#$).concat(Object.keys(this.#i)).forEach(($)=>{i[$]||=this.#v($)}),this.#i=this.#$=void 0,_V(),i}#v(i){let $=[],n=i===x;if([this.#i,this.#$].forEach((g)=>{let v=g[i]?Object.keys(g[i]).map((r)=>[r,g[i][r]]):[];if(v.length!==0)n||=!0,$.push(...v);else if(i!==x)$.push(...Object.keys(g[x]).map((r)=>[r,g[x][r]]))}),!n)return null;else return DV($)}};var kV=class{name="PreparedRegExpRouter";#i;#$;constructor(i,$){this.#i=i,this.#$=$}#v(i,$){let n=this.#i[i];n[1].forEach((g)=>g&&g.push($)),Object.values(n[2]).forEach((g)=>g[0].push($))}#r(i,$,n,g,v){let r=this.#i[i];if(!v)r[2][$][0].push([n,{}]);else g.forEach((b)=>{if(typeof b==="number")r[1][b].push([n,v]);else r[2][b||$][0].push([n,v])})}add(i,$,n){if(!this.#i[i]){let v=this.#i[x],r={};for(let b in v[2])r[b]=[v[2][b][0].slice(),kn];this.#i[i]=[v[0],v[1].map((b)=>Array.isArray(b)?b.slice():0),r]}if($==="/*"||$==="*"){let v=[n,{}];if(i===x)for(let r in this.#i)this.#v(r,v);else this.#v(i,v);return}let g=this.#$[$];if(!g)throw Error(`Path ${$} is not registered`);for(let[v,r]of g)if(i===x)for(let b in this.#i)this.#r(b,$,n,v,r);else this.#r(i,$,n,v,r)}buildAllMatchers(){return this.#i}match=Qr};var m_=class{name="SmartRouter";#i=[];#$=[];constructor(i){this.#i=i.routers}add(i,$,n){if(!this.#$)throw Error(Xr);this.#$.push([i,$,n])}match(i,$){if(!this.#$)throw Error("Fatal error");let n=this.#i,g=this.#$,v=n.length,r=0,b;for(;r<v;r++){let u=n[r];try{for(let U=0,I=g.length;U<I;U++)u.add(...g[U]);b=u.match(i,$)}catch(U){if(U instanceof fr)continue;throw U}this.match=u.match.bind(u),this.#i=[u],this.#$=void 0;break}if(r===v)throw Error("Fatal error");return this.name=`SmartRouter + ${this.activeRouter.name}`,b}get activeRouter(){if(this.#$||this.#i.length!==1)throw Error("No active router has been determined yet.");return this.#i[0]}};var On=Object.create(null),wV=(i)=>{for(let $ in i)return!0;return!1},b2=class i{#i;#$;#v;#r=0;#g=On;constructor($,n,g){if(this.#$=g||Object.create(null),this.#i=[],$&&n){let v=Object.create(null);v[$]={handler:n,possibleKeys:[],score:0},this.#i=[v]}this.#v=[]}insert($,n,g){this.#r=++this.#r;let v=this,r=nk(n),b=[];for(let u=0,U=r.length;u<U;u++){let I=r[u],_=r[u+1],k=gk(I,_),O=Array.isArray(k)?k[0]:I;if(O in v.#$){if(v=v.#$[O],k)b.push(k[1]);continue}if(v.#$[O]=new i,k)v.#v.push(k),b.push(k[1]);v=v.#$[O]}return v.#i.push({[$]:{handler:g,possibleKeys:b.filter((u,U,I)=>I.indexOf(u)===U),score:this.#r}}),v}#n($,n,g,v,r){for(let b=0,u=n.#i.length;b<u;b++){let U=n.#i[b],I=U[g]||U[x],_={};if(I!==void 0){if(I.params=Object.create(null),$.push(I),v!==On||r&&r!==On)for(let k=0,O=I.possibleKeys.length;k<O;k++){let J=I.possibleKeys[k],Q=_[I.score];I.params[J]=r?.[J]&&!Q?r[J]:v[J]??r?.[J],_[I.score]=!0}}}}search($,n){let g=[];this.#g=On;let r=[this],b=g_(n),u=[],U=b.length,I=null;for(let _=0;_<U;_++){let k=b[_],O=_===U-1,J=[];for(let A=0,z=r.length;A<z;A++){let B=r[A],Ii=B.#$[k];if(Ii)if(Ii.#g=B.#g,O){if(Ii.#$["*"])this.#n(g,Ii.#$["*"],$,B.#g);this.#n(g,Ii,$,B.#g)}else J.push(Ii);for(let y=0,N=B.#v.length;y<N;y++){let C=B.#v[y],ui=B.#g===On?{}:{...B.#g};if(C==="*"){let i$=B.#$["*"];if(i$)this.#n(g,i$,$,B.#g),i$.#g=ui,J.push(i$);continue}let[Wn,Vn,bv]=C;if(!k&&!(bv instanceof RegExp))continue;let _i=B.#$[Wn];if(bv instanceof RegExp){if(I===null){I=Array(U);let fn=n[0]==="/"?1:0;for(let uv=0;uv<U;uv++)I[uv]=fn,fn+=b[uv].length+1}let i$=n.substring(I[_]),Xn=bv.exec(i$);if(Xn){if(ui[Vn]=Xn[0],this.#n(g,_i,$,B.#g,ui),Xn[0].length===i$.length&&_i.#$["*"])this.#n(g,_i.#$["*"],$,B.#g,ui);if(wV(_i.#$)){_i.#g=ui;let fn=Xn[0].match(/\//)?.length??0;(u[fn]||=[]).push(_i)}continue}}if(bv===!0||bv.test(k))if(ui[Vn]=k,O){if(this.#n(g,_i,$,ui,B.#g),_i.#$["*"])this.#n(g,_i.#$["*"],$,ui,B.#g)}else _i.#g=ui,J.push(_i)}}let Q=u.shift();r=Q?J.concat(Q):J}if(g.length>1)g.sort((_,k)=>{return _.score-k.score});return[g.map(({handler:_,params:k})=>[_,k])]}};var y_=class{name="TrieRouter";#i;constructor(){this.#i=new b2}add(i,$,n){let g=Ir($);if(g){for(let v=0,r=g.length;v<r;v++)this.#i.insert(i,g[v],n);return}this.#i.insert(i,$,n)}match(i,$){return this.#i.search(i,$)}};var Jn=class extends $2{constructor(i={}){super(i);this.router=i.router??new m_({routers:[new Gr,new y_]})}};var jV=class{initApp;#i;constructor(i){this.initApp=i?.initApp,this.#i=i?.defaultAppOptions}createApp=(i)=>{let $=new Jn(i&&this.#i?{...this.#i,...i}:i??this.#i);if(this.initApp)this.initApp($);return $};createMiddleware=(i)=>i;createHandlers=(...i)=>{return i.filter(($)=>$!==void 0)}},u2=(i)=>new jV(i);var c=u2();var U2=(i)=>{let $=Buffer.byteLength(i.content,"utf-8");if(i.meta)for(let[n,g]of Object.entries(i.meta))$+=Buffer.byteLength(n,"utf-8")+Buffer.byteLength(g,"utf-8");return $},OV=1048576,JV=200,PV=4194304,WV=()=>{};class h_{clients=new Map;subscribers=new Set;logger;onError;maxBufferedBytes;now;replayBufferSize;replayBufferMaxBytes;replayBuffer=[];persistentReplay;exclusiveCursor=new Map;replayBufferBytes=0;eventsBroadcast=0;droppedSlowClients=0;lastBroadcastAt=null;latestOffset=0;constructor(i={}){this.logger=i.logger,this.onError=i.onError??WV,this.maxBufferedBytes=i.maxBufferedBytes??OV,this.now=i.now??(()=>Date.now()),this.replayBufferSize=Math.max(0,i.replayBufferSize??JV),this.replayBufferMaxBytes=Math.max(0,i.replayBufferMaxBytes??PV),this.persistentReplay=i.persistentReplay??null}getMetrics(){return{clients:this.clients.size,subscribers:this.subscribers.size,eventsBroadcast:this.eventsBroadcast,droppedSlowClients:this.droppedSlowClients,lastBroadcastAt:this.lastBroadcastAt?new Date(this.lastBroadcastAt).toISOString():null,latestOffset:this.latestOffset,oldestReplayableOffset:this.replayBuffer[0]?.offset??null}}replaySince(i,$){let n=this.replayBuffer[0]?.offset,g=this.persistentReplay&&(n===void 0||i<n-1),v=this.replayBuffer.filter((U)=>U.offset>i&&this.matchesClient(U,$));if(!g)return v;let r=this.persistentReplay?this.persistentReplay.loadSince(i).filter((U)=>this.matchesClient(U,$)):[],b=n??Number.POSITIVE_INFINITY;return[...r.filter((U)=>U.offset<b),...v]}matchesClient(i,$){let n=i.meta?.target;if(n&&n!==$.subscriberId)return!1;let g=i.meta?.channelId;if(g&&g!==$.channel)return!1;let v=i.meta?.connector;if(!v)return!0;return $.connectors.includes(v)}pickRecipients(i){let $=new Map,n=[];for(let[g,v]of this.clients){if(!this.matchesClient(i,v))continue;if(v.delivery==="exclusive"){let r=$.get(v.channel)??[];r.push(g),$.set(v.channel,r);continue}n.push(g)}for(let[g,v]of $){if(v.length===0)continue;let r=this.exclusiveCursor.get(g)??0,b=v[r%v.length];if(b)n.push(b);this.exclusiveCursor.set(g,r+1)}return n}addClient(i,$){this.clients.set(i,$)}removeClient(i){this.clients.delete(i)}getClientCount(){return this.clients.size}listChannels(){return[...this.clients.values()].map((i)=>({...i}))}subscribe(i){return this.subscribers.add(i),()=>{this.subscribers.delete(i)}}broadcast(i,$){this.latestOffset+=1;let n={content:i,meta:$,offset:this.latestOffset},g=JSON.stringify(n);if(this.eventsBroadcast+=1,this.lastBroadcastAt=this.now(),this.replayBufferSize>0){let r=U2(n);this.replayBuffer.push(n),this.replayBufferBytes+=r;while((this.replayBuffer.length>this.replayBufferSize||this.replayBufferBytes>this.replayBufferMaxBytes)&&this.replayBuffer.length>0){let b=this.replayBuffer.shift();if(b)this.replayBufferBytes-=U2(b)}}let v=this.pickRecipients(n);for(let r of v){let b=r.getBufferedAmount();if(b>this.maxBufferedBytes){let u=this.clients.get(r);this.logger?.warn("dropping slow WS client (backpressure)",{channel:u?.channel,buffered:b,max:this.maxBufferedBytes});try{r.close(1009,"backpressure")}catch{}this.clients.delete(r),this.droppedSlowClients+=1;continue}try{r.send(g)}catch(u){let U=u instanceof Error?u:Error(String(u)),I=this.clients.get(r);this.logger?.warn("ws.send failed; dropping client",{channel:I?.channel,error:U.message}),this.clients.delete(r)}}for(let r of this.subscribers){let b=(u)=>{let U=u instanceof Error?u:Error(String(u));this.logger?.error("broadcast subscriber threw",{error:U.message}),this.onError(U,{component:"broadcaster.subscriber",offset:n.offset,connector:n.meta?.connector??null,channel:n.meta?.channel??null})};try{let u=r(n);if(u instanceof Promise)u.catch(b)}catch(u){b(u)}}return n}seedLatestOffset(i){if(i>this.latestOffset)this.latestOffset=i}}var H3=D.object({type:D.string(),content:D.string(),channel_id:D.string().nullable(),connector_id:D.string().nullable(),meta:D.record(D.string(),D.string()).nullable()});class d_{}import{Database as VV}from"bun:sqlite";import{existsSync as XV,mkdirSync as fV}from"fs";import{dirname as QV}from"path";var KV=/^[a-z_][a-z0-9_]*$/,GV=500,YV=new Set(["seq","ts","type","event"]),I2=[["CREATE TABLE IF NOT EXISTS logs (seq INTEGER PRIMARY KEY, ts INTEGER NOT NULL, type TEXT, event TEXT NOT NULL)","CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts)","CREATE INDEX IF NOT EXISTS idx_logs_type ON logs (type)"],["DROP TABLE IF EXISTS leuco_log","CREATE TABLE IF NOT EXISTS logs (seq INTEGER PRIMARY KEY, ts INTEGER NOT NULL, type TEXT, event TEXT NOT NULL)","CREATE INDEX IF NOT EXISTS idx_logs_ts ON logs (ts)","CREATE INDEX IF NOT EXISTS idx_logs_type ON logs (type)"]];class V${db;maxRows;maxAgeMs;maxBytes;targetBytes;now;indexes;extractIndexes;insertStmt;insertWithSeqStmt;maxSeqStmt;countStmt;trimRowsStmt;trimAgeStmt;trimOldestStmt;insertsSinceByteCheck=0;constructor(i){if(i.path!==":memory:"){let r=QV(i.path);if(!XV(r))fV(r,{recursive:!0})}if(this.db=new VV(i.path),this.db.run("PRAGMA journal_mode = WAL"),this.migrate(),this.maxRows=i.maxRows??null,this.maxAgeMs=i.maxAgeMs??null,this.maxBytes=i.maxBytes??null,this.targetBytes=i.targetBytes??(i.maxBytes!==void 0?Math.floor(i.maxBytes/4):null),this.now=i.now??(()=>Date.now()),this.indexes=i.indexes??[],this.indexes.length>0)zV(this.indexes),this.extractIndexes=i.extractIndexes??null,this.syncIndexColumns();else this.extractIndexes=null;let $=["ts","type","event",...this.indexes],n=$.map(()=>"?").join(", ");this.insertStmt=this.db.prepare(`INSERT INTO logs (${$.join(", ")}) VALUES (${n})`);let g=["seq",...$],v=g.map(()=>"?").join(", ");this.insertWithSeqStmt=this.db.prepare(`INSERT INTO logs (${g.join(", ")}) VALUES (${v})`),this.maxSeqStmt=this.db.prepare("SELECT COALESCE(MAX(seq), 0) AS max FROM logs"),this.countStmt=this.db.prepare("SELECT COUNT(*) AS n FROM logs"),this.trimRowsStmt=this.db.prepare("DELETE FROM logs WHERE seq <= (SELECT seq FROM logs ORDER BY seq DESC LIMIT 1 OFFSET ?)"),this.trimAgeStmt=this.db.prepare("DELETE FROM logs WHERE ts < ?"),this.trimOldestStmt=this.db.prepare("DELETE FROM logs WHERE seq IN (SELECT seq FROM logs ORDER BY seq ASC LIMIT ?)")}insert(i){try{let $=this.buildInsertParams(i.ts,i.event),n=this.insertStmt.run(...$),g=Number(n.lastInsertRowid);return this.trim(),{seq:g,ts:i.ts,event:i.event}}catch($){return $ instanceof Error?$:Error(String($))}}insertMany(i){if(i.length===0)return[];try{let $=[];return this.db.transaction((g)=>{for(let v of g){let r=this.buildInsertParams(v.ts,v.event),b=this.insertStmt.run(...r);$.push({seq:Number(b.lastInsertRowid),ts:v.ts,event:v.event})}})(i),this.trim(),$}catch($){return $ instanceof Error?$:Error(String($))}}write(i){try{let $=[i.seq,...this.buildInsertParams(i.ts,i.event)];this.insertWithSeqStmt.run(...$),this.trim()}catch($){return $ instanceof Error?$:Error(String($))}}getMaxSeq(){let i=this.maxSeqStmt.get();return i?i.max:0}query(i={}){let $=[],n=[];if(i.seq!==void 0)$.push("seq = ?"),n.push(i.seq);else $.push("seq > ?"),n.push(i.sinceSeq??0);if(typeof i.type==="string")$.push("type = ?"),n.push(i.type);if(i.where)this.appendWhereConditions(i.where,$,n);if(i.wherePrefix)this.appendPrefixConditions(i.wherePrefix,$,n);if(i.whereIn)this.appendSetConditions(i.whereIn,$,n);let g=i.limit??1000;n.push(g);let v=i.order==="desc"?"DESC":"ASC",r=`SELECT seq, ts, type, event FROM logs WHERE ${$.join(" AND ")} ORDER BY seq ${v} LIMIT ?`,u=this.db.prepare(r).all(...n);if(v==="DESC")u.reverse();return u.map(AV)}getSchemaVersion(){return this.db.prepare("PRAGMA user_version").get()?.user_version??0}close(){this.db.close()}buildInsertParams(i,$){let n=SV($),g=JSON.stringify($);if(this.indexes.length===0)return[i,n,g];let v=this.extractIndexes?this.extractIndexes($):null,r=this.indexes.map((b)=>v?.[b]??null);return[i,n,g,...r]}appendWhereConditions(i,$,n){for(let g of this.indexKeys()){let v=i[g];if(v===void 0)continue;if(v===null)$.push(`${g} IS NULL`);else $.push(`${g} = ?`),n.push(v)}}appendPrefixConditions(i,$,n){for(let g of this.indexKeys()){let v=i[g];if(v===void 0)continue;$.push(`${g} LIKE ? ESCAPE '\\'`),n.push(`${HV(v)}%`)}}appendSetConditions(i,$,n){for(let g of this.indexKeys()){let v=i[g];if(v===void 0)continue;if(v.length===0){$.push("1 = 0");continue}let r=v.filter((U)=>U!==null),b=r.length!==v.length,u=[];if(r.length>0)u.push(`${g} IN (${r.map(()=>"?").join(", ")})`),n.push(...r);if(b)u.push(`${g} IS NULL`);$.push(`(${u.join(" OR ")})`)}}trim(){if(this.maxRows!==null){let i=this.countStmt.get();if(i&&i.n>this.maxRows)this.trimRowsStmt.run(this.maxRows)}if(this.maxAgeMs!==null)this.trimAgeStmt.run(this.now()-this.maxAgeMs);this.maybeTrimBytes()}indexKeys(){return this.indexes}maybeTrimBytes(){if(this.maxBytes===null||this.targetBytes===null)return;if(this.insertsSinceByteCheck+=1,this.insertsSinceByteCheck<GV)return;this.insertsSinceByteCheck=0;let i=this.byteSize();if(i<=this.maxBytes)return;let $=this.countStmt.get()?.n??0;if($===0)return;let n=i-this.targetBytes,g=i/$,v=Math.min($,Math.ceil(n/g));this.trimOldestStmt.run(v),this.db.run("VACUUM")}byteSize(){let i=this.db.prepare("PRAGMA page_count").get()?.n??0,$=this.db.prepare("PRAGMA page_size").get()?.n??0;return i*$}clear(){this.db.run("DELETE FROM logs"),this.db.run("VACUUM"),this.insertsSinceByteCheck=0}syncIndexColumns(){let i=new Set(this.db.prepare("PRAGMA table_info(logs)").all().map(($)=>$.name));for(let $ of this.indexes){if(!i.has($))this.db.run(`ALTER TABLE logs ADD COLUMN ${$} TEXT`);this.db.run(`CREATE INDEX IF NOT EXISTS idx_logs_${$} ON logs (${$})`)}}migrate(){let $=this.db.prepare("PRAGMA user_version").get()?.user_version??0;if($>=I2.length)return;let n=I2.slice($),g=$;for(let v of n)g+=1,this.db.transaction(()=>{for(let b of v)this.db.run(b);this.db.run(`PRAGMA user_version = ${g}`)})()}}function HV(i){return i.replaceAll("\\","\\\\").replaceAll("%","\\%").replaceAll("_","\\_")}function zV(i){for(let $ of i){if(!KV.test($))throw Error(`invalid index column name: ${$}`);if(YV.has($))throw Error(`reserved index column name: ${$}`)}}function SV(i){if(typeof i!=="object"||i===null)return null;if(!("type"in i))return null;let $=i.type;return typeof $==="string"?$:null}function AV(i){return{seq:i.seq,ts:i.ts,event:JSON.parse(i.event)}}var _2=2000;class a_ extends d_{sink;now;logger;onError;constructor(i){super();this.now=i.now??(()=>Date.now()),this.logger=i.logger,this.onError=i.onError,this.sink=new V$({path:i.path,indexes:["channel_id","connector_id"],extractIndexes:($)=>({channel_id:$.channel_id,connector_id:$.connector_id}),now:this.now,...i.maxRows!==void 0?{maxRows:i.maxRows}:{},...i.maxAgeMs!==void 0?{maxAgeMs:i.maxAgeMs}:{},...i.maxBytes!==void 0?{maxBytes:i.maxBytes}:{},...i.targetBytes!==void 0?{targetBytes:i.targetBytes}:{}})}record(i){let $={type:i.meta?.event_type??"unknown",content:BV(i.content),channel_id:i.channelId,connector_id:i.connectorId,meta:i.meta},n=this.sink.write({seq:i.offset,ts:this.now(),event:$});if(n instanceof Error)this.logger?.error("event log write failed",{offset:i.offset,error:n.message}),this.onError?.(n,{component:"sqlite-event-log",op:"record",offset:i.offset,channelId:i.channelId,connectorId:i.connectorId})}loadSince(i){let $=this.sink.query({sinceSeq:i,limit:Number.MAX_SAFE_INTEGER}),n=[];for(let g of $)n.push({content:g.event.content,meta:g.event.meta??void 0,offset:g.seq});return n}loadForChannel(i){let $={channel_id:i.channelId};if(i.connectorId!==void 0)$.connector_id=i.connectorId;let n=this.sink.query({where:$,...i.sinceSeq!==void 0?{sinceSeq:i.sinceSeq}:{},...i.limit!==void 0?{limit:i.limit}:{}}),g=[];for(let v of n)g.push({content:v.event.content,meta:v.event.meta??void 0,offset:v.seq});return g}findMaxOffset(){return this.sink.getMaxSeq()}clear(){this.sink.clear()}close(){this.sink.close()}}function BV(i){if(i.length<=_2)return i;return`${i.slice(0,_2)}...`}var lV=()=>{},LV=30000,TV=60000,ZV=30000,qV=(i)=>new Promise(($)=>{setTimeout($,i)});class ti{channels;notify;logger;onError;running=new Map;failureCounts=new Map;starting=new Set;stats=new Map;healthCheckIntervalMs;maxBackoffMs;startTimeoutMs;sleep;now;healthCheckTimer=null;healthCheckInFlight=!1;pendingRetry=new Map;constructor(i){this.channels=i.channels,this.notify=i.notify,this.logger=i.logger,this.onError=i.onError??lV,this.healthCheckIntervalMs=i.healthCheckIntervalMs??LV,this.maxBackoffMs=i.maxBackoffMs??TV,this.startTimeoutMs=i.startTimeoutMs??ZV,this.sleep=i.sleep??qV,this.now=i.now??(()=>Date.now())}static keyOf(i,$){return`${i}/${$}`}isRunning(i,$){return this.running.has(ti.keyOf(i,$))}list(){return[...this.running.entries()].map(([i,$])=>{let n=this.stats.get(i);return{channelName:$.channelName,channelId:$.channelId,name:$.config.name,type:$.config.type,alive:$.listener.isAlive(),events:n?.events??0,errors:n?.errors??0,failureCount:this.failureCounts.get(i)??0,lastEventAt:n?.lastEventAt??null}})}async start(i,$){let n=ti.keyOf(i,$);if(this.running.has(n))return{ok:!0,reason:"already running"};if(this.starting.has(n))return{ok:!1,reason:"already starting",retriable:!0};this.starting.add(n);try{return await this.startLocked(i,$,n)}finally{this.starting.delete(n)}}async startLocked(i,$,n){let g=this.channels.createListener(i,$);if(!g)return{ok:!1,reason:`connector "${$}" not found in channel "${i}"`};let v=async(r,b)=>{try{await this.notify(i,$,r,b),this.recordEvent(n)}catch(u){throw this.recordError(n),u}};try{return await Promise.race([g.listener.start(v),this.sleep(this.startTimeoutMs).then(()=>{throw Error(`listener start timed out after ${this.startTimeoutMs}ms`)})]),this.running.set(n,{config:g.config,channelName:i,channelId:g.channelId,listener:g.listener}),this.pendingRetry.delete(n),this.ensureStats(n),this.logger?.info(`${g.config.type} listener started`,{channel:i,connector:$}),{ok:!0}}catch(r){let b=r instanceof Error?r:Error(String(r));try{await g.listener.stop()}catch{}this.logger?.error(`${g.config.type} listener failed to start`,{channel:i,connector:$,error:b.message}),this.onError(b,{component:"listener-registry.start",channel:i,connector:$,type:g.config.type});let u=!(b instanceof $n);return{ok:!1,reason:b.message,retriable:u}}}async stop(i,$){let n=ti.keyOf(i,$),g=this.running.get(n);if(!g)return{ok:!0,reason:"not running"};try{return await g.listener.stop(),this.logger?.info(`${g.config.type} listener stopped`,{channel:i,connector:$}),{ok:!0}}catch(v){let r=v instanceof Error?v:Error(String(v));return this.logger?.error(`${g.config.type} listener failed to stop`,{channel:i,connector:$,error:r.message}),this.onError(r,{component:"listener-registry.stop",channel:i,connector:$,type:g.config.type}),{ok:!1,reason:r.message}}finally{this.running.delete(n),this.failureCounts.delete(n)}}async restart(i,$){let n=await this.stop(i,$);if(!n.ok)return n;return await this.start(i,$)}async startAll(){let i=this.channels.listAllConnectors(),$=await Promise.allSettled(i.map((n)=>this.start(n.channelName,n.name)));for(let n=0;n<$.length;n++){let g=$[n],v=i[n];if(g.status==="rejected"){let r=ti.keyOf(v.channelName,v.name);this.pendingRetry.set(r,{channelName:v.channelName,connectorName:v.name});continue}if(g.status==="fulfilled"&&!g.value.ok){if(g.value.retriable===!1)continue;let r=ti.keyOf(v.channelName,v.name);this.pendingRetry.set(r,{channelName:v.channelName,connectorName:v.name})}}this.startHealthCheck()}async stopAll(){this.stopHealthCheck(),this.pendingRetry.clear();for(let[,i]of this.running.entries())await this.stop(i.channelName,i.config.name)}ensureStats(i){let $=this.stats.get(i);if($)return $;let n={events:0,errors:0,failureCount:0,lastEventAt:null};return this.stats.set(i,n),n}recordEvent(i){let $=this.ensureStats(i);$.events+=1,$.lastEventAt=new Date(this.now()).toISOString()}recordError(i){this.ensureStats(i).errors+=1}startHealthCheck(){if(this.healthCheckTimer)return;this.healthCheckTimer=setInterval(()=>{this.runHealthCheck().catch((i)=>{let $=i instanceof Error?i:Error(String(i));this.logger?.error("health check pass failed",{error:$.message}),this.onError($,{component:"listener-registry.health-check"})})},this.healthCheckIntervalMs),this.healthCheckTimer.unref()}stopHealthCheck(){if(!this.healthCheckTimer)return;clearInterval(this.healthCheckTimer),this.healthCheckTimer=null}async runHealthCheckForTest(){await this.runHealthCheck()}async runHealthCheck(){if(this.healthCheckInFlight)return;this.healthCheckInFlight=!0;try{let i=[];for(let[n,g]of this.running.entries()){if(g.listener.isAlive()){this.failureCounts.delete(n);continue}i.push({channelName:g.channelName,connectorName:g.config.name,type:g.config.type})}await Promise.all(i.map((n)=>this.recoverDead(n.channelName,n.connectorName,n.type)));let $=[];for(let[n,g]of this.pendingRetry.entries()){if(this.running.has(n)){this.pendingRetry.delete(n);continue}$.push({key:n,channelName:g.channelName,connectorName:g.connectorName})}await Promise.all($.map((n)=>this.attemptRetry(n)))}finally{this.healthCheckInFlight=!1}}async attemptRetry(i){this.logger?.info("retrying failed listener",{channel:i.channelName,connector:i.connectorName});let $=this.failureCounts.get(i.key)??0,n=Math.min(1000*2**$,this.maxBackoffMs);await this.sleep(n);let g=await this.start(i.channelName,i.connectorName);if(g.ok){this.pendingRetry.delete(i.key),this.failureCounts.delete(i.key);return}if(g.retriable===!1){this.pendingRetry.delete(i.key),this.failureCounts.delete(i.key),this.logger?.warn("dropping listener from retry queue (non-retriable)",{channel:i.channelName,connector:i.connectorName,reason:g.reason});return}this.failureCounts.set(i.key,$+1)}async recoverDead(i,$,n){let g=ti.keyOf(i,$),v=this.failureCounts.get(g)??0,r=Math.min(1000*2**v,this.maxBackoffMs);this.logger?.warn(`${n} listener unhealthy, restarting`,{channel:i,connector:$,attempt:v+1,backoffMs:r}),await this.stop(i,$),await this.sleep(r);let b=await this.start(i,$);if(b.ok)this.failureCounts.delete(g),this.logger?.info(`${n} listener recovered`,{channel:i,connector:$});else if(b.retriable===!1)this.failureCounts.delete(g),this.logger?.warn(`${n} listener cannot recover (non-retriable)`,{channel:i,connector:$,reason:b.reason});else this.failureCounts.set(g,v+1)}}var MV=new R,xV=3000,NV=100,EV=200,tV=(i)=>new Promise(($)=>{setTimeout($,i)}),cV=(i)=>`funnel-gateway[${i}]`,CV=async(i)=>{let $=i.now()+xV;while(i.now()<$){if(i.pids.every((n)=>!i.runner.isAlive(n)))return;await i.sleep(NV)}for(let n of i.pids){if(!i.runner.isAlive(n))continue;try{i.runner.kill(n,"SIGKILL")}catch{}}await i.sleep(EV)},D2=async(i)=>{let $=i.process??MV,n=i.logger,g=cV(i.dir),v=$.listProcessesContaining(g),r=[];for(let b of v){if(b.pid===i.selfPid)continue;$.kill(b.pid,"SIGTERM"),r.push(b.pid),n?.info("killed competing Slack gateway process",{pid:b.pid,args:b.command.slice(0,160)})}if(r.length===0)return r;return await CV({runner:$,pids:r,sleep:i.sleep??tV,now:i.now??(()=>Date.now())}),r};var k2=(i,$,n)=>{let g=i.req.raw.headers.get("Cookie");if(typeof $==="string"){if(!g)return;let r=$;if(n==="secure")r="__Secure-"+$;else if(n==="host")r="__Host-"+$;return b_(g,r)[r]}if(!g)return{};return b_(g)};var RV=/^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i,FV=/^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/i,oV=/^application\/x-www-form-urlencoded(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/i,p_=(i,$)=>{return async(n,g)=>{let v={},r=n.req.header("Content-Type");switch(i){case"json":if(!r||!RV.test(r))break;try{v=await n.req.json()}catch{throw new si(400,{message:"Malformed JSON in request body"})}break;case"form":{if(!r||!(FV.test(r)||oV.test(r)))break;let u;if(n.req.bodyCache.formData)u=await n.req.bodyCache.formData;else try{let I=await n.req.arrayBuffer();u=await Wr(I,r),n.req.bodyCache.formData=u}catch(I){let _="Malformed FormData request.";throw _+=I instanceof Error?` ${I.message}`:` ${String(I)}`,new si(400,{message:_})}let U=Object.create(null);u.forEach((I,_)=>{if(_.endsWith("[]"))(U[_]??=[]).push(I);else if(Array.isArray(U[_]))U[_].push(I);else if(Object.hasOwn(U,_))U[_]=[U[_],I];else U[_]=I}),v=U;break}case"query":v=Object.fromEntries(Object.entries(n.req.queries()).map(([u,U])=>{return U.length===1?[u,U[0]]:[u,U]}));break;case"param":v=n.req.param();break;case"header":v=n.req.header();break;case"cookie":v=k2(n);break}let b=await $(v,n);if(b instanceof Response)return b;return n.req.addValidatedData(i,b),await g()}};function mV(i,$,n,g){return p_(i,async(v,r)=>{let b=v;if(i==="header"&&"_def"in $||i==="header"&&"_zod"in $){let U=Object.keys("in"in $?$.in.shape:$.shape),I=Object.fromEntries(U.map((_)=>[_.toLowerCase(),_]));b=Object.fromEntries(Object.entries(v).map(([_,k])=>[I[_]||_,k]))}let u=g&&g.validationFunction?await g.validationFunction($,b):await $.safeParseAsync(b);if(n){let U=await n({data:b,...u,target:i},r);if(U){if(U instanceof Response)return U;if("response"in U)return U.response}}if(!u.success)return r.json(u,400);return u.data})}var Yr=mV;var Ki=(i)=>Yr("param",i,($,n)=>{if($.success)return;let g=$.error.issues[0],v=g?`${g.path.join(".")}: ${g.message}`:"invalid request";return n.json({ok:!1,reason:v},400)});var e_=D.lazy(()=>D.union([D.string(),D.number(),D.boolean(),D.null(),D.array(e_),D.record(D.string(),e_)])),yV=D.object({method:D.string().min(1),path:D.string().min(1),body:e_.optional()}),w2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=null;try{n=await i.req.json()}catch{n=null}let g=yV.safeParse(n);if(!g.success)throw new si(400,{message:g.error.issues[0]?.message??"invalid body"});let v=await i.var.deps.channels.call($.channel,$.connector,{method:g.data.method,path:g.data.path,body:g.data.body});return i.json({ok:!0,result:v})});var j2=c.createHandlers(Ki(D.object({channel:D.string().min(1)})),Yr("json",ck,(i,$)=>{if(i.success)return;let n=i.error.issues[0],g=n?`${n.path.join(".")}: ${n.message}`:"invalid body";return $.json({ok:!1,reason:g},400)}),(i)=>{let $=i.req.valid("param"),n=i.req.valid("json"),g=n.target?{...n.meta,target:n.target}:n.meta,r={ok:!0,offset:i.var.deps.emit({channel:$.channel,connector:n.connector,content:n.content,meta:g}).offset};return i.json(r)});var hV=(i)=>{let n=(i.connectionErrors[i.connectionErrors.length-1]??null)?.detail??null;if(i.connectors.length>0&&!i.listener)return{status:"error",message:"no listener running for this channel",nextActions:["fnl gateway restart"],rootCause:n};if(i.listener&&!i.listener.alive)return{status:"error",message:"listener is dead",nextActions:["fnl gateway logs","fnl gateway restart"],rootCause:n};if(i.claudeClients===0)return{status:"warn",message:"no Claude connected to this channel",nextActions:[`fnl claude --channel ${i.name}`],rootCause:null};if(i.listener&&i.listener.errors>0)return{status:"warn",message:"listener has errors",nextActions:["fnl gateway logs"],rootCause:n};return{status:"ok",message:"healthy",nextActions:[],rootCause:null}},O2=c.createHandlers(async(i)=>{let $=i.var.deps,n=i.req.query("channel")??null,g=$.channels.list(),v=n?g.filter((I)=>I.name===n||I.id===n):g,r=$.registry.list(),b=$.broadcaster.listChannels(),u=$.broadcaster.getMetrics(),U=v.map((I)=>{let _=r.find((B)=>B.channelName===I.name)??null,k=_?{alive:_.alive,events:_.events,errors:_.errors,lastEventAt:_.lastEventAt}:null,O=b.filter((B)=>B.channel===I.id||B.channel===I.name).length,J=(()=>{if(!$.diagnosticLog)return[];try{return $.diagnosticLog.queryProcessed({channelId:I.id,limit:10}).map(vv)}catch{return[]}})(),Q=k&&(!k.alive||k.errors>0)||!k,A=(()=>{if(!Q||!$.diagnosticLog)return[];try{return $.diagnosticLog.queryConnection({channelId:I.id,statuses:["auth-failed","error"],limit:3}).map(P$)}catch{return[]}})(),z={id:I.id,name:I.name,connectors:I.connectors.map((B)=>B.name),listener:k,claudeClients:O,recentEvents:J,connectionErrors:A};return{...z,diagnosis:hV(z)}});return i.json({pid:$.selfPid,uptimeMs:$.uptimeMs(),eventsBroadcast:u.eventsBroadcast,channels:U})});var J2=c.createHandlers((i)=>{let $=i.var.deps;return i.json({ok:!0,pid:$.selfPid,funnelDir:$.dir,clients:$.broadcaster.getClientCount(),listeners:$.registry.list()})});var P2=c.createHandlers((i)=>{return i.json({listeners:i.var.deps.registry.list()})});var W2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=await i.var.deps.registry.restart($.channel,$.connector);return i.json(n,n.ok?200:400)});var V2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=await i.var.deps.registry.start($.channel,$.connector);return i.json(n,n.ok?200:400)});var X2=c.createHandlers(Ki(D.object({channel:D.string().min(1),connector:D.string().min(1)})),async(i)=>{let $=i.req.valid("param"),n=await i.var.deps.registry.stop($.channel,$.connector);return i.json(n,n.ok?200:400)});var f2=c.createHandlers((i)=>{let $=i.var.deps;return i.json({ok:!0,pid:$.selfPid,funnelDir:$.dir,uptimeMs:$.uptimeMs(),clients:$.broadcaster.listChannels(),listeners:$.registry.list(),broadcaster:$.broadcaster.getMetrics()})});function dV(){return c.createApp().onError((i,$)=>{if(i instanceof si)return i.getResponse();let n=i instanceof Error?i.message:String(i);return $.json({error:n},500)}).get("/health",...J2).get("/status",...f2).get("/debug",...O2).get("/listeners",...P2).post("/listeners/:channel/:connector/start",...V2).delete("/listeners/:channel/:connector",...X2).post("/listeners/:channel/:connector/restart",...W2).post("/channels/:channel/connectors/:connector/call",...w2).post("/channels/:channel/publish",...j2)}var Q2=dV();import{createHash as aV,randomUUID as pV}from"crypto";import{join as eV}from"path";function K2(i){let $=i.port===0?pV():String(i.port),n=aV("sha256").update(`${i.funnelDir}\x00${$}`).digest("hex").slice(0,20);return eV(i.tmpDir,"events",`${n}.db`)}var vX="127.0.0.1",nX=new Set(["127.0.0.1","localhost","::1","::ffff:127.0.0.1"]),gX=()=>{};class s_{channels;configuredPort;configuredHostname;dbPath;process;logger;onError;selfPid;dir;killCompetingSlack;token;allowInsecureHost;broadcaster;eventLog;registry;nowMs;extraRoutes;ownsEventLog;diagnosticLog;startedAt=null;server=null;disposed=!1;constructor(i){this.channels=i.channels,this.configuredPort=i.port??Mi(),this.configuredHostname=i.hostname??vX,this.dir=i.dir??ji,this.dbPath=i.dbPath??K2({tmpDir:i.tmpDir??fi(),funnelDir:this.dir,port:this.configuredPort}),this.process=i.process,this.logger=i.logger,this.onError=i.onError??gX,this.selfPid=i.selfPid??globalThis.process.pid,this.killCompetingSlack=i.killCompetingSlack??!0,this.token=i.token??"",this.allowInsecureHost=i.allowInsecureHost??!1,this.extraRoutes=i.extraRoutes??null,this.diagnosticLog=i.diagnosticLog;let $=i.clock;if(this.nowMs=$?()=>$.millis():()=>Date.now(),i.eventLog)this.eventLog=i.eventLog,this.ownsEventLog=!1;else{let n=$X(this.dbPath);if(!sV(n))iX(n,{recursive:!0});this.eventLog=new a_({path:this.dbPath,now:this.nowMs,logger:this.logger,onError:this.onError}),this.ownsEventLog=!0}this.broadcaster=new h_({logger:this.logger,onError:this.onError,now:this.nowMs,persistentReplay:this.eventLog}),this.broadcaster.seedLatestOffset(this.eventLog.findMaxOffset()),this.registry=new ti({channels:this.channels,logger:this.logger,onError:this.onError,notify:async(n,g,v,r)=>{this.emit({channel:n,connector:g,content:v,meta:r})},now:this.nowMs})}get port(){return this.server?.port??this.configuredPort}get hostname(){return this.server?.hostname??this.configuredHostname}async start(){if(this.disposed)throw Error("FunnelGatewayServer is single-use: construct a new instance to start again");if(this.server)return;if(!this.token&&!nX.has(this.configuredHostname)&&!this.allowInsecureHost)throw Error(`refusing to start gateway: hostname "${this.configuredHostname}" is reachable off-box but no token is set. Set a token, bind to loopback (127.0.0.1), or pass allowInsecureHost: true.`);let i=this.buildApp();await this.killCompetingSlackIfNeeded(),this.startedAt=this.nowMs(),this.server=Bun.serve({port:this.configuredPort,hostname:this.configuredHostname,development:!1,fetch:($,n)=>this.handleFetch($,n,i),websocket:{open:($)=>this.handleWsOpen($),close:($)=>this.handleWsClose($),message(){}}}),this.logServerStarted();try{await this.bootListeners()}catch($){throw this.server.stop(),this.server=null,$}}async stop(){if(await this.registry.stopAll(),this.server)this.server.stop(),this.server=null;if(this.ownsEventLog)this.eventLog.close();this.disposed=!0}getStatus(){return{clients:this.broadcaster.getClientCount(),channels:this.broadcaster.listChannels()}}getBroadcaster(){return this.broadcaster}getRegistry(){return this.registry}getEventLog(){return this.eventLog}onEvent(i){return this.broadcaster.subscribe(i)}handleFetch(i,$,n){let g=new URL(i.url);if(g.pathname==="/ws"&&i.headers.get("upgrade")==="websocket"){if(this.token&&!this.tokenMatchesUpgrade(i))return new Response("unauthorized",{status:401});let v=g.searchParams.get("channel")??"",r=v?this.resolveChannel(v):null;if(v&&!r)return new Response(`unknown channel "${v}"`,{status:404});let b=r?.id??v,u=r?.name??null,U=r?.connectors??[],I=r?.delivery??"fanout",_=g.searchParams.get("since"),k=_===null?Number.NaN:Number.parseInt(_,10),O=Number.isFinite(k)&&k>=0?k:void 0,J=g.searchParams.get("id")??void 0;if($.upgrade(i,{data:{channel:b,channelName:u,connectors:U,delivery:I,subscriberId:J,since:O}}))return;return new Response("WebSocket upgrade failed",{status:400})}return n.fetch(i)}handleWsOpen(i){if(typeof i.data.since==="number"){let $=this.broadcaster.replaySince(i.data.since,i.data);try{for(let n of $)i.send(JSON.stringify(n))}catch(n){let g=n instanceof Error?n:Error(String(n));this.logger?.warn("replay send failed during ws.open",{error:g.message}),this.onError(g,{component:"gateway-server.replay"});return}}this.broadcaster.addClient(i,i.data),this.logger?.info("channel connected",{event_type:"system",action:"channel_connect",channel:i.data.channelName??"",channelId:i.data.channel,connectors:i.data.connectors.join(","),total:String(this.broadcaster.getClientCount())})}handleWsClose(i){this.broadcaster.removeClient(i),this.logger?.info("channel disconnected",{event_type:"system",action:"channel_disconnect",channel:i.data.channelName??"",channelId:i.data.channel,total:String(this.broadcaster.getClientCount())})}logServerStarted(){this.logger?.info("gateway started",{event_type:"system",action:"gateway_start",port:String(this.port),pid:String(this.selfPid)}),this.logger?.info("funnel gateway listening",{url:`http://localhost:${this.port}`,websocket:`ws://localhost:${this.port}/ws`,health:`http://localhost:${this.port}/health`})}buildApp(){let i=c.createApp();if(i.use((n,g)=>{return n.set("deps",{selfPid:this.selfPid,dir:this.dir,broadcaster:this.broadcaster,registry:this.registry,channels:this.channels,uptimeMs:()=>this.startedAt?this.nowMs()-this.startedAt:0,emit:(v)=>this.emit(v),diagnosticLog:this.diagnosticLog}),g()}),this.token)i.use("/listeners/*",Ei({expected:this.token})),i.use("/status",Ei({expected:this.token})),i.use("/debug",Ei({expected:this.token})),i.use("/channels/*",Ei({expected:this.token}));return(this.extraRoutes?i.route("/",this.extraRoutes):i).route("/",Q2)}tokenMatchesUpgrade(i){let $=(i.headers.get("sec-websocket-protocol")??"").split(",").map((v)=>v.trim()).filter((v)=>v.length>0);for(let v of $)if(v.startsWith("funnel.token.")&&Pr(v.slice(13),this.token))return!0;let g=(i.headers.get("authorization")??"").match(/^Bearer\s+(.+)$/i);if(g&&Pr(g[1]??"",this.token))return!0;return!1}resolveChannel(i){let $=this.channels.get(i)??this.channels.getById(i);if(!$)return null;return{id:$.id,name:$.name,connectors:$.connectors.map((n)=>n.name),delivery:$.delivery}}async killCompetingSlackIfNeeded(){if(!this.killCompetingSlack)return;if(!this.channels.listAllConnectors().some((n)=>n.type==="slack"))return;let $=await D2({selfPid:this.selfPid,dir:this.dir,process:this.process,logger:this.logger});if($.length>0)this.logger?.info("killed competing Slack gateway processes",{event_type:"system",action:"kill_competing",pids:$.join(",")})}async bootListeners(){await this.registry.startAll();for(let i of this.registry.list())this.logger?.info(`${i.type} listener started: ${i.name}`,{event_type:"system",action:`${i.type}_connect`,channel:i.channelName,connector:i.name});this.logger?.info(`event store: ${this.dbPath}`),this.logger?.info("funnel gateway running")}emit(i){let $=this.lookupChannelId(i.channel),n=$&&i.connector?this.lookupConnectorId($,i.connector):null,g={...i.meta,channel:i.channel};if(i.connector)g.connector=i.connector;if($)g.channelId=$;if(n)g.connectorId=n;let v=this.broadcaster.broadcast(i.content,g);return this.eventLog.record({content:i.content,channelId:$??null,connectorId:n??null,meta:g,offset:v.offset}),{offset:v.offset}}lookupChannelId(i){return this.channels.get(i)?.id??this.channels.getById(i)?.id??null}lookupConnectorId(i,$){return this.channels.getById(i)?.connectors.find((g)=>g.name===$)?.id??null}}import{homedir as rX}from"os";import{dirname as bX,join as G2}from"path";var Y2="gateway.token",uX=32,UX=new F,IX=()=>{let i=new Uint8Array(uX);return crypto.getRandomValues(i),[...i].map(($)=>$.toString(16).padStart(2,"0")).join("")};class i1{fs;path;generate;constructor(i={}){this.fs=i.fs??UX,this.path=G2(i.dir??ji,Y2),this.generate=i.generate??IX,Object.freeze(this)}read(){if(!this.fs.existsSync(this.path))return null;let i=this.fs.readFileSync(this.path).trim();return i.length>0?i:null}ensure(){return this.fs.mkdirSync(bX(this.path),{recursive:!0}),this.fs.withFileLock(`${this.path}.lock`,()=>{let i=this.read();if(i)return i;let $=this.generate();return this.fs.writeSecretFileSync(this.path,`${$}
796
796
  `),$})}getPath(){return this.path}}var IS=G2(rX(),".funnel",Y2);var _X=D.object({channelName:D.string(),channelId:D.string(),name:D.string(),type:D.string(),alive:D.boolean()}),DX=D.object({listeners:D.array(_X)}),kX=D.object({reason:D.string().optional()}),$1={state:"offline"};class v1{port;isDaemonRunning;getToken;constructor(i){this.port=i.port,this.isDaemonRunning=i.isDaemonRunning,this.getToken=i.getToken??(()=>null),Object.freeze(this)}async list(){if(!this.isDaemonRunning())return{state:"offline"};try{let i=await J$(`${xi(this.port)}/listeners`,{headers:this.authHeaders()});if(!i.ok)return{state:"error",reason:`HTTP ${i.status}`};let $=DX.safeParse(await i.json());if(!$.success)return{state:"error",reason:"malformed daemon response"};return{state:"ok",listeners:$.data.listeners}}catch(i){return{state:"error",reason:i instanceof Error?i.message:String(i)}}}async start(i,$){if(!this.isDaemonRunning())return $1;return await this.call("POST",`/listeners/${this.path(i,$)}/start`)}async stop(i,$){if(!this.isDaemonRunning())return $1;return await this.call("DELETE",`/listeners/${this.path(i,$)}`)}async restart(i,$){if(!this.isDaemonRunning())return $1;return await this.call("POST",`/listeners/${this.path(i,$)}/restart`)}path(i,$){return`${encodeURIComponent(i)}/${encodeURIComponent($)}`}authHeaders(){let i=this.getToken();return i?{authorization:`Bearer ${i}`}:{}}async call(i,$){try{let n=await J$(`${xi(this.port)}${$}`,{method:i,headers:this.authHeaders()},30000);if(!n.ok){let g=null;try{g=await n.json()}catch{g=null}let v=kX.safeParse(g);return{state:"error",reason:(v.success?v.data.reason:void 0)??`HTTP ${n.status}`}}return{state:"ok"}}catch(n){return{state:"error",reason:n instanceof Error?n.message:String(n)}}}}var jX="/sandbox/.funnel",OX="/sandbox/tmp",JX=()=>{};class Hr{paths;channels;gateway;gatewayToken;publisher;listeners;claude;profiles;localConfig;localConfigSync;diagnostics;recovery;doctor;docs;fs;process;logger;clock;http;onError;diagnosticLog;constructor(i={}){let $=i.dir??ur(),n=i.tmpDir??fi(),g=i.fs??new F,v=i.process??new R,r=i.clock??new Ni,b=i.idGenerator??new Zi,u=i.http??new Di;this.paths={dir:$,tmpDir:n,settings:wX($,"settings.json")},this.fs=g,this.process=v,this.logger=i.logger,this.clock=r,this.http=u,this.onError=i.onError??JX,this.diagnosticLog=i.diagnosticLog;let U=i.store??new v_({path:this.paths.settings,fs:g,idGenerator:b}),I=new D_({descriptors:i.connectors??[],fs:g,process:v,http:u,clock:r,logger:this.logger,diagnosticLog:this.diagnosticLog,signal:i.signal,dir:$});this.profiles=new Y_({store:U,idGenerator:b,fs:g}),this.channels=new k_({store:U,registry:I,clock:r,idGenerator:b,profileChecker:this.profiles}),this.gateway=new c_({fs:g,process:v,clock:r,dir:$,tmpDir:n,port:i.port}),this.gatewayToken=new i1({fs:g,dir:$}),this.publisher=new E_({port:this.gateway.getPort(),isDaemonRunning:()=>this.gateway.isRunning(),getToken:()=>this.gatewayToken.read()}),this.listeners=new v1({port:this.gateway.getPort(),isDaemonRunning:()=>this.gateway.isRunning(),getToken:()=>this.gatewayToken.read()});let _=new G_({fs:g});this.localConfig=new f_({fs:g}),this.localConfigSync=new K_({channels:this.channels,prompter:i.tokenPrompter??new z_});let k=new O_({global:this.profiles,local:new j_({fs:g,dir:$})});this.claude=new w_({channels:this.channels,mcp:_,gateway:this.gateway,sessions:k,guard:new kr({fs:g,process:v,dir:$}),process:v,logger:this.logger,dir:$}),this.diagnostics=new P_({gateway:this.gateway,gatewayToken:this.gatewayToken,channels:this.channels,publisher:this.publisher,diagnosticLog:this.diagnosticLog,tmpDir:n}),this.recovery=new H_({gateway:this.gateway,listeners:this.listeners,channels:this.channels}),this.doctor=new W_({diagnostics:this.diagnostics,recovery:this.recovery}),this.docs=new X_,Object.freeze(this)}static inMemory(i={}){return new Hr({...i,store:i.store??new L_,fs:i.fs??new S_,process:i.process??new l_,logger:i.logger??new B_,clock:i.clock??new T_,idGenerator:i.idGenerator??new A_,http:i.http??new Z_,tokenPrompter:i.tokenPrompter??new q_,diagnosticLog:i.diagnosticLog??new N_,dir:i.dir??jX,tmpDir:i.tmpDir??OX})}gatewayServer(i={}){return new s_({channels:this.channels,port:i.port,hostname:i.hostname,...i.eventLog?{eventLog:i.eventLog}:{dbPath:i.dbPath},process:this.process,clock:this.clock,logger:this.logger,onError:this.onError,dir:this.paths.dir,tmpDir:this.paths.tmpDir,killCompetingSlack:i.killCompetingSlack,token:i.token??this.gatewayToken.ensure(),allowInsecureHost:i.allowInsecureHost,extraRoutes:i.extraRoutes,diagnosticLog:this.diagnosticLog})}createProcessGuard(){return new kr({fs:this.fs,process:this.process,dir:this.paths.dir})}async runGatewayForeground(i={}){let $=Jr(),g=i.caffeinate!==!1&&globalThis.process.platform==="darwin"?["caffeinate","-is","bun",$]:["bun",$];return this.process.attach(g,{env:{FUNNEL_DIR:this.paths.dir,FUNNEL_PORT:String(this.gateway.getPort()),FUNNEL_TMP_DIR:this.paths.tmpDir}})}gatewayClient(){let{port:i}=this.gateway.getStatus();return __(xi(i))}}import{appendFileSync as WX,mkdirSync as VX}from"fs";import{dirname as XX,join as fX}from"path";var PX=[/xox[abprs]-[A-Za-z0-9-]{10,}/g,/xapp-[A-Za-z0-9-]{10,}/g,/gh[pousr]_[A-Za-z0-9]{20,}/g,/github_pat_[A-Za-z0-9_]{20,}/g,/Bot [A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{20,}/g,/Bearer [A-Za-z0-9._~+/-]{16,}/g],H2=(i)=>{let $=i;for(let n of PX)$=$.replace(n,"[redacted]");return $};var QX=()=>fX(fi(),"funnel.log");class n1 extends In{file;now;constructor(i={}){super();this.file=i.file??QX(),this.now=i.now??(()=>new Date),Object.freeze(this)}info(i,$){this.write("info",i,$)}warn(i,$){this.write("warn",i,$)}error(i,$){this.write("error",i,$)}write(i,$,n){VX(XX(this.file),{recursive:!0});let g={time:this.now().toISOString(),level:i,message:$,...n?{meta:n}:{}};WX(this.file,`${H2(JSON.stringify(g))}
797
797
  `)}}var S2=(i)=>{let $=new Jn;if(i.token)$.use("/diagnostics",Ei({expected:i.token})),$.use("/diagnostics/*",Ei({expected:i.token})),$.use("/doctor",Ei({expected:i.token}));return $.get("/diagnostics",async(n)=>{let g=n.req.query("channel");if(n.req.query("all")!==void 0){let b=await i.diagnostics.diagnoseAll();return n.json(b)}let r=await i.diagnostics.diagnose(g??void 0);if(!r)return n.json({error:"channel not found"},404);return n.json(r)}),$.get("/diagnostics/events",async(n)=>{let g=n.req.query("channel")??null,v=n.req.query("connector"),r=Number(n.req.query("limit")??"20"),b=await i.diagnostics.recentEvents(g,{connector:v,limit:r});return n.json(b)}),$.get("/diagnostics/dropped",async(n)=>{let g=n.req.query("channel")??null,v=n.req.query("connector"),r=Number(n.req.query("limit")??"20"),b=await i.diagnostics.droppedEvents(g,{connector:v,limit:r});return n.json(b)}),$.get("/diagnostics/errors",async(n)=>{let g=n.req.query("channel")??null,v=n.req.query("connector"),r=Number(n.req.query("limit")??"20"),b=await i.diagnostics.connectionErrors(g,{connector:v,limit:r});return n.json(b)}),$.get("/diagnostics/raw",async(n)=>{let g=n.req.query("channel")??null,v=n.req.query("connector"),r=Number(n.req.query("limit")??"20"),b=await i.diagnostics.rawEvents(g,{connector:v,limit:r});return n.json(b)}),$.get("/diagnostics/connection",async(n)=>{let g=n.req.query("channel")??null,v=n.req.query("connector"),r=Number(n.req.query("limit")??"20"),b=await i.diagnostics.connectionTimeline(g,{connector:v,limit:r});return n.json(b)}),$.get("/diagnostics/logs",async(n)=>{let g=n.req.query("grep")??void 0,v=Number(n.req.query("limit")??"200"),r=await i.diagnostics.recentLogs({grep:g,limit:v});return n.json(r)}),$.post("/diagnostics/replay",async(n)=>{let g=await z2(n),v=typeof g.channel==="string"?g.channel:null,r=typeof g.seq==="number"?g.seq:void 0;if(!v)return n.json({error:"channel is required"},400);let b=await i.diagnostics.replay(v,r);return n.json(b)}),$.post("/doctor",async(n)=>{let g=await z2(n),v=g.mode==="safe"||g.mode==="aggressive"||g.mode==="off"?g.mode:"off",r=await i.doctor.run(v);return n.json(r)}),$},KX=(i)=>i!==null&&typeof i==="object"&&!Array.isArray(i),z2=async(i)=>{try{let $=await i.req.json();return KX($)?$:{}}catch{return{}}};var A2=(i)=>{if(!(i instanceof Error))return!1;if(("code"in i?String(Reflect.get(i,"code")):"")==="EADDRINUSE")return!0;let n=i.message;return n.includes("EADDRINUSE")||n.includes("address already in use")||n.includes("in use")};import{chmodSync as GX}from"fs";var YX=262144;class b1 extends _n{raw;processed;connection;now;logger;constructor(i){super();this.now=i.now??(()=>Date.now()),this.logger=i.logger;let $=i.maxAgeMs!==void 0?{maxAgeMs:i.maxAgeMs}:{},n={now:this.now,...$,...i.maxRows!==void 0?{maxRows:i.maxRows}:{}},g=i.rawMaxRows??i.maxRows,v={now:this.now,...$,...g!==void 0?{maxRows:g}:{}};this.raw=new V$({path:i.rawPath,indexes:["event_id","connector_id","channel_id"],extractIndexes:(r)=>({event_id:r.event_id,connector_id:r.connector_id,channel_id:r.channel_id}),...v}),this.processed=new V$({path:i.processedPath,indexes:["event_id","connector_id","channel_id","outcome"],extractIndexes:(r)=>({event_id:r.event_id,connector_id:r.connector_id,channel_id:r.channel_id,outcome:r.outcome}),...n}),this.connection=new V$({path:i.connectionPath,indexes:["connector_id","channel_id","status"],extractIndexes:(r)=>({connector_id:r.connector_id,channel_id:r.channel_id,status:r.status}),...n}),g1(i.rawPath),g1(i.processedPath),g1(i.connectionPath),Object.freeze(this)}recordRaw(i){let $={event_id:i.eventId,type:i.type,connector_id:i.connectorId,channel_id:i.channelId,payload:SX(i.payload,i.type)};this.report("raw",this.raw.insert({ts:this.now(),event:$}))}recordProcessed(i){let $={event_id:i.eventId,type:i.type,connector_id:i.connectorId,channel_id:i.channelId,outcome:i.outcome,payload:i.payload};this.report("processed",this.processed.insert({ts:this.now(),event:$}))}recordConnection(i){let $={type:i.type,connector_id:i.connectorId,channel_id:i.channelId,status:i.status,detail:i.detail};this.report("connection",this.connection.insert({ts:this.now(),event:$}))}report(i,$){if($ instanceof Error)this.logger?.error("diagnostic log insert failed",{table:i,error:$.message})}queryRaw(i){let $=r1(i);if(i.eventId!==void 0)$.event_id=i.eventId;return this.raw.query({...i.type!==void 0?{type:i.type}:{},...i.seq!==void 0?{seq:i.seq}:{},...i.limit!==void 0?{limit:i.limit}:{},where:$,order:"desc"}).map((g)=>({seq:g.seq,ts:g.ts,eventId:g.event.event_id,type:g.event.type,connectorId:g.event.connector_id,channelId:g.event.channel_id,payload:g.event.payload}))}queryProcessed(i){let $=r1(i);if(i.eventId!==void 0)$.event_id=i.eventId;if(i.outcome!==void 0)$.outcome=i.outcome;return this.processed.query({...i.type!==void 0?{type:i.type}:{},...i.seq!==void 0?{seq:i.seq}:{},...i.limit!==void 0?{limit:i.limit}:{},where:$,...i.outcomePrefix!==void 0?{wherePrefix:{outcome:i.outcomePrefix}}:{},order:"desc"}).map((g)=>({seq:g.seq,ts:g.ts,eventId:g.event.event_id,type:g.event.type,connectorId:g.event.connector_id,channelId:g.event.channel_id,outcome:g.event.outcome,payload:g.event.payload}))}queryConnection(i){let $=r1(i);if(i.status!==void 0)$.status=i.status;return this.connection.query({...i.type!==void 0?{type:i.type}:{},...i.seq!==void 0?{seq:i.seq}:{},...i.limit!==void 0?{limit:i.limit}:{},where:$,...i.statuses!==void 0?{whereIn:{status:i.statuses}}:{},order:"desc"}).map((g)=>({seq:g.seq,ts:g.ts,type:g.event.type,connectorId:g.event.connector_id,channelId:g.event.channel_id,status:zX(g.event.status),detail:g.event.detail}))}clear(){this.raw.clear(),this.processed.clear(),this.connection.clear()}close(){this.raw.close(),this.processed.close(),this.connection.close()}}var g1=(i)=>{if(i===":memory:")return;for(let $ of["","-wal","-shm"])try{GX(`${i}${$}`,384)}catch{}},r1=(i)=>{let $={};if(i.connectorId!==void 0)$.connector_id=i.connectorId;if(i.channelId!==void 0)$.channel_id=i.channelId;return $},HX=tk.shape.status,zX=(i)=>{let $=HX.safeParse(i);return $.success?$.data:"error"},SX=(i,$)=>{let n=Buffer.byteLength(i,"utf8");if(n<=YX)return i;return JSON.stringify({...BX(i),_funnel_oversized:n,_funnel_type:$})},AX=["type","subtype","ts","channel","channel_type","user","bot_id"],BX=(i)=>{try{let $=JSON.parse(i);if(typeof $!=="object"||$===null)return{};let n=$,g={};for(let v of AX)if(n[v]!==void 0)g[v]=n[v];return g}catch{return{}}};import{join as u1}from"path";var B2=(i)=>({rawPath:u1(i,"connector-raw.db"),processedPath:u1(i,"connector-processed.db"),connectionPath:u1(i,"connector-connection.db")});var ZX=5000,qX=50000,MX=604800000,U1=Mi(),xX=process.env.FUNNEL_HOST||"127.0.0.1",Pn=ur(),zr=TX(Pn,"gateway.pid");process.title=`funnel-gateway[${Pn}]`;var rv=new n1,NX=new R;T2(Pn,{recursive:!0});var l2=()=>{try{return LX(zr,String(process.pid),{flag:"wx"}),!0}catch(i){if(!(i instanceof Error)||!("code"in i)||i.code!=="EEXIST")throw i;return!1}};if(!l2()){let i=Number(lX(zr,"utf-8").trim());if(i>0&&NX.isAlive(i))rv.error("funnel gateway already running",{pid:i}),process.exit(1);if(Z2(zr),!l2())rv.error("funnel gateway PID file claimed by another daemon mid-race"),process.exit(1)}process.on("exit",()=>{try{Z2(zr)}catch{}});var q2=fi();T2(q2,{recursive:!0});var M2=new b1({...B2(q2),rawMaxRows:ZX,maxRows:qX,maxAgeMs:MX,logger:rv});process.on("exit",()=>{try{M2.close()}catch{}});var Sr=new Hr({logger:rv,diagnosticLog:M2,dir:Pn,connectors:sD()}),x2=Sr.gatewayToken.ensure(),EX=S2({diagnostics:Sr.diagnostics,doctor:Sr.doctor,token:x2}),N2=Sr.gatewayServer({port:U1,hostname:xX,extraRoutes:EX,token:x2});try{await N2.start()}catch(i){if(A2(i))rv.error(`gateway port ${U1} is already in use by another funnel daemon (a different repo/scope). Set FUNNEL_PORT to a distinct port, or stop the other daemon.`,{port:U1,dir:Pn});else rv.error("gateway failed to start",{error:i instanceof Error?i.message:String(i)});process.exit(1)}var L2=!1,E2=async(i)=>{if(L2)return;L2=!0;try{await Promise.race([N2.stop(),new Promise(($)=>setTimeout($,3000))])}catch{}process.exit(i)};process.on("SIGINT",()=>{E2(130)});process.on("SIGTERM",()=>{E2(143)});
package/dist/gateway.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as gatewayLoopbackUrl } from "./loopback-fetch-HP-bo9Kv.js";
2
- import { S as connectorRawEventSchema, _ as MemoryConnectorDiagnosticLog, a as DEFAULT_GATEWAY_TOKEN_PATH, b as connectorConnectionEventSchema, c as FunnelListenerRegistry, d as funnelEventSchema, f as FunnelBroadcaster, g as publishResponseSchema, h as publishRequestSchema, i as channelWsUrl, l as SqliteFunnelEventLog, m as FunnelChannelPublisher, n as MemoryFunnelEventLog, o as FunnelGatewayToken, r as channelWsProtocols, s as FunnelGatewayServer, t as SqliteConnectorDiagnosticLog, u as FunnelEventLog, v as CONNECTOR_CONNECTION_STATUSES, x as connectorProcessedEventSchema, y as ConnectorDiagnosticLog } from "./sqlite-diagnostic-log-Qs4cf9I_.js";
2
+ import { S as connectorRawEventSchema, _ as MemoryConnectorDiagnosticLog, a as DEFAULT_GATEWAY_TOKEN_PATH, b as connectorConnectionEventSchema, c as FunnelListenerRegistry, d as funnelEventSchema, f as FunnelBroadcaster, g as publishResponseSchema, h as publishRequestSchema, i as channelWsUrl, l as SqliteFunnelEventLog, m as FunnelChannelPublisher, n as MemoryFunnelEventLog, o as FunnelGatewayToken, r as channelWsProtocols, s as FunnelGatewayServer, t as SqliteConnectorDiagnosticLog, u as FunnelEventLog, v as CONNECTOR_CONNECTION_STATUSES, x as connectorProcessedEventSchema, y as ConnectorDiagnosticLog } from "./sqlite-diagnostic-log-B4p3bdCq.js";
3
3
  export { CONNECTOR_CONNECTION_STATUSES, ConnectorDiagnosticLog, DEFAULT_GATEWAY_TOKEN_PATH, FunnelBroadcaster, FunnelChannelPublisher, FunnelEventLog, FunnelGatewayServer, FunnelGatewayToken, FunnelListenerRegistry, MemoryConnectorDiagnosticLog, MemoryFunnelEventLog, SqliteConnectorDiagnosticLog, SqliteFunnelEventLog, channelWsProtocols, channelWsUrl, connectorConnectionEventSchema, connectorProcessedEventSchema, connectorRawEventSchema, funnelEventSchema, gatewayLoopbackUrl, publishRequestSchema, publishResponseSchema };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { a as FunnelConnectorTypeMismatchError, c as FunnelTokenCollisionError,
7
7
  import { a as FunnelMcp, o as FunnelFileProcessGuard, s as FunnelClaude, t as renderYaml } from "./yaml-render-DxxRQT-V.js";
8
8
  import { n as sessionFileExists, t as FunnelProfiles } from "./profiles-KpN7_gBw.js";
9
9
  import { a as queryRows, i as previewOf, n as diagnosticEventOfProcessed, o as toDiagnosticConnectionError, r as diagnosticEventOfRaw, s as toDiagnosticEvent, t as diagnosticConnectionEventOf } from "./diagnostic-event-CxMM5Bl2.js";
10
- import { t as FunnelDiagnostics } from "./funnel-diagnostics-7rV3HxY3.js";
10
+ import { t as FunnelDiagnostics } from "./funnel-diagnostics-DremV1of.js";
11
11
  import { t as FunnelDoctor } from "./funnel-doctor-Csw5qjcH.js";
12
12
  import { t as FunnelDocs } from "./funnel-docs-CI4hMkhq.js";
13
13
  import { a as FunnelTokenPrompter, i as FunnelLocalConfigSync, n as MemoryFunnelTokenPrompter, o as FunnelLocalConfig, r as NodeFunnelTokenPrompter, t as funnelJsonSchema } from "./local-config-json-schema-_-YVHX4c.js";
@@ -15,7 +15,7 @@ import { t as discordConnectorSchema } from "./discord-connector-schema-saAGuPmJ
15
15
  import { t as ghConnectorSchema } from "./gh-connector-schema-DmZTrzUu.js";
16
16
  import { t as slackConnectorSchema } from "./slack-connector-schema-EGVuuqSX.js";
17
17
  import { t as FunnelRecovery } from "./funnel-recovery-DYbSYGWC.js";
18
- import { C as funnelTmpDir, S as connectorRawEventSchema, _ as MemoryConnectorDiagnosticLog, a as DEFAULT_GATEWAY_TOKEN_PATH, b as connectorConnectionEventSchema, c as FunnelListenerRegistry, d as funnelEventSchema, f as FunnelBroadcaster, g as publishResponseSchema, h as publishRequestSchema, i as channelWsUrl, l as SqliteFunnelEventLog, m as FunnelChannelPublisher, n as MemoryFunnelEventLog, o as FunnelGatewayToken, p as requireBearerToken, r as channelWsProtocols, s as FunnelGatewayServer, t as SqliteConnectorDiagnosticLog, u as FunnelEventLog, v as CONNECTOR_CONNECTION_STATUSES, x as connectorProcessedEventSchema, y as ConnectorDiagnosticLog } from "./sqlite-diagnostic-log-Qs4cf9I_.js";
18
+ import { C as funnelTmpDir, S as connectorRawEventSchema, _ as MemoryConnectorDiagnosticLog, a as DEFAULT_GATEWAY_TOKEN_PATH, b as connectorConnectionEventSchema, c as FunnelListenerRegistry, d as funnelEventSchema, f as FunnelBroadcaster, g as publishResponseSchema, h as publishRequestSchema, i as channelWsUrl, l as SqliteFunnelEventLog, m as FunnelChannelPublisher, n as MemoryFunnelEventLog, o as FunnelGatewayToken, p as requireBearerToken, r as channelWsProtocols, s as FunnelGatewayServer, t as SqliteConnectorDiagnosticLog, u as FunnelEventLog, v as CONNECTOR_CONNECTION_STATUSES, x as connectorProcessedEventSchema, y as ConnectorDiagnosticLog } from "./sqlite-diagnostic-log-B4p3bdCq.js";
19
19
  import { t as FunnelConnectorAdapter } from "./connector-adapter-BL2ehMej.js";
20
20
  import { t as FunnelConnectorListener } from "./connector-listener-BcQMzY6Q.js";
21
21
  import { t as FunnelSlackEventProcessor } from "./slack-event-processor-Cs_YuI3w.js";