corsair 0.1.79 → 0.1.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-T3RG3J3R.js → chunk-GJ743ML7.js} +11 -11
- package/dist/core.js +1 -1
- package/dist/hub.js +1 -1
- package/dist/index.js +1 -1
- package/dist/oauth.js +1 -1
- package/dist/setup.d.ts +2 -2
- package/dist/setup.js +1 -1
- package/dist/tunnel.js +1 -1
- package/package.json +1 -1
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import{a as ie,b as ot,c as P,d as B,e as it,f as z,g as U,h as se,i as Re,j as st,k as kn,l as at,m as ct}from"./chunk-6D4UDUPJ.js";import{a as rt}from"./chunk-IGGCNGU2.js";import*as mr from"querystring";var Fe=["ahrefs","airtable","amplitude","asana","bitwarden","bluesky","box","cal","calendly","cloudflare","cursor","discord","dodopayments","dropbox","exa","figma","firecrawl","fireflies","github","gitlab","gmail","googlecalendar","googledrive","googlesheets","grafana","hackernews","hubspot","intercom","jira","linear","monday","notion","onedrive","openweathermap","oura","outlook","pagerduty","posthog","razorpay","reddit","resend","sentry","sharepoint","slack","spotify","strava","stripe","tally","tavily","teams","telegram","todoist","trello","twilio","twitter","twitterapiio","typeform","vapi","xquik","youtube","zendesk","zohomail","zoom"],ut={ahrefs:"Ahrefs",airtable:"Airtable",amplitude:"Amplitude",asana:"Asana",bitwarden:"Bitwarden",bluesky:"Bluesky",box:"Box",cal:"Cal",calendly:"Calendly",cloudflare:"Cloudflare",cursor:"Cursor",discord:"Discord",dodopayments:"Dodo Payments",dropbox:"Dropbox",exa:"Exa",figma:"Figma",firecrawl:"Firecrawl",fireflies:"Fireflies",github:"GitHub",gitlab:"GitLab",gmail:"Gmail",googlecalendar:"Google Calendar",googledrive:"Google Drive",googlesheets:"Google Sheets",grafana:"Grafana",hackernews:"Hacker News",hubspot:"HubSpot",intercom:"Intercom",jira:"Jira",linear:"Linear",monday:"Monday",notion:"Notion",onedrive:"OneDrive",openweathermap:"OpenWeatherMap",oura:"Oura",outlook:"Outlook",pagerduty:"PagerDuty",posthog:"PostHog",razorpay:"Razorpay",reddit:"Reddit",resend:"Resend",sentry:"Sentry",sharepoint:"SharePoint",slack:"Slack",spotify:"Spotify",strava:"Strava",stripe:"Stripe",tally:"Tally",tavily:"Tavily",teams:"Teams",telegram:"Telegram",todoist:"Todoist",trello:"Trello",twilio:"Twilio",twitter:"Twitter",twitterapiio:"Twitter API IO",typeform:"Typeform",vapi:"Vapi",xquik:"XQuik",youtube:"YouTube",zendesk:"Zendesk",zohomail:"Zoho Mail",zoom:"Zoom"};function ve(e){let n=ut[e];return n||e.charAt(0).toUpperCase()+e.slice(1)}var Ie="https://auth.corsair.dev";var V=class extends Error{constructor(){super("Hub is not configured. Pass hub: { projectApiKey, signingSecret, deliveryUrl } to createCorsair()."),this.name="HubNotConfiguredError"}};function Be(e){let n=(e.apiUrl?.trim()||Ie).replace(/\/$/,""),t=e.projectApiKey.trim(),r=e.signingSecret.trim(),o=e.deliveryUrl.trim();if(!t||!r||!o)throw new Error("Hub config requires non-empty projectApiKey, signingSecret, and deliveryUrl");return{apiUrl:n,projectApiKey:t,signingSecret:r,deliveryUrl:o,oauthCallbackUrl:e.oauthCallbackUrl?.trim()}}function br(e){return e.apiUrl.trim().length>0&&e.deliveryUrl.trim().length>0&&e.projectApiKey.trim().length>0&&e.signingSecret.trim().length>0}function M(e){let n=P(e).hub;if(!n||!br(n))throw new V;return n}function Ue(e){return e.oauthCallbackUrl?e.oauthCallbackUrl:`${e.apiUrl.replace(/\/$/,"")}/oauth/callback`}function N(e){return typeof e=="string"&&e.length>0}function dt(e){if(!e||typeof e!="object")throw new Error("Hub API returned an empty connect session");let n=e;if(!N(n.connectUrl)||!N(n.token)||!N(n.projectId))throw new Error("Hub API returned an incomplete connect session (expected connectUrl, token, and projectId)");let t={connectUrl:n.connectUrl,token:n.token,projectId:n.projectId};return N(n.expiresAt)&&(t.expiresAt=n.expiresAt),t}function lt(e){if(!e||typeof e!="object")throw new Error("Hub API returned an empty permission session");let n=e;if(!N(n.approvalUrl)||!N(n.token)||!N(n.projectId)||!N(n.expiresAt))throw new Error("Hub API returned an incomplete permission session (expected approvalUrl, token, projectId, and expiresAt)");return{approvalUrl:n.approvalUrl,token:n.token,projectId:n.projectId,expiresAt:n.expiresAt}}function pt(e){if(!e||typeof e!="object")throw new Error("Hub token refresh returned an empty response");let n=e;if(!N(n.access_token))throw new Error("Hub token refresh returned no access_token");return{access_token:n.access_token,refresh_token:N(n.refresh_token)?n.refresh_token:void 0,expires_in:typeof n.expires_in=="number"?n.expires_in:void 0,scope:N(n.scope)?n.scope:void 0}}function gt(e){if(!e||typeof e!="object")return null;let n=e;return n.error??n.message??null}function wr(e){return e.replace(/\/$/,"")}async function Cr(e){let n=e.headers.get("content-type")??"",t=await e.text();if(!t)return null;if(!n.includes("application/json")&&t.trimStart().startsWith("<"))throw new Error(`Hub API returned HTML instead of JSON (HTTP ${e.status}). Check HUB_API_URL and deploy the latest hub API.`);try{return JSON.parse(t)}catch{throw new Error(`Hub API returned invalid JSON (HTTP ${e.status})`)}}function Tr(e,n,t){if(n===404&&t)return t;let r=gt(e);return r||`Hub API returned HTTP ${n}`}async function me(e){let n=wr(e.hub.apiUrl),t=await fetch(`${n}${e.path}`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.hub.projectApiKey}`},body:JSON.stringify(e.body)}),r=await Cr(t);if(!t.ok)throw new Error(Tr(r,t.status,e.notFoundMessage));return e.parseResponse(r)}import*as Se from"crypto";function J(e,n){return Buffer.from(JSON.stringify({plugin:e,tenantId:n,iat:Date.now()})).toString("base64url")}function bn(e,{maxAgeMs:n}={}){try{let t=e.includes(".")?e.split(".")[0]:e,r=JSON.parse(Buffer.from(t,"base64url").toString("utf-8"));if(r!==null&&typeof r=="object"&&"plugin"in r&&"tenantId"in r&&typeof r.plugin=="string"&&typeof r.tenantId=="string"){let o=r;return n!==void 0&&typeof o.iat=="number"&&Date.now()-o.iat>n?null:o}return null}catch{return null}}function ae(e,n){let t=Se.createHmac("sha256",n).update(e).digest("base64url");return`${e}.${t}`}var Pr=600*1e3;function Ne(e,n){let t=e.lastIndexOf(".");if(t===-1)return null;let r=e.slice(0,t),o=e.slice(t+1),i=Se.createHmac("sha256",n).update(r).digest("base64url"),s=Buffer.from(o,"base64url"),a=Buffer.from(i,"base64url");return s.length!==a.length||!Se.timingSafeEqual(s,a)?null:bn(r,{maxAgeMs:Pr})}var xr=new Set(["localhost","127.0.0.1","[::1]","::1"]);function _e(e){try{let{hostname:n,protocol:t}=new URL(e);return t!=="http:"&&t!=="https:"?!1:xr.has(n)}catch{return!1}}function ke(e){return _e(e)?"client":"server"}function ft(e,n){return e==="client"||_e(n)}function be(e){if(!e.source)return null;let n=_e(e.deliveryUrl);return e.source==="server"&&n?{error:'source "server" cannot be used with a loopback delivery URL \u2014 omit source to auto-detect or use "client"',status:400}:e.oauthMode==="managed"&&e.source==="client"&&!n?{error:'managed OAuth with source "client" requires a loopback delivery URL \u2014 omit source for production server delivery',status:400}:null}import{ZodBoolean as
|
|
2
|
-
`)}function Cn(e,n,t){let r=t==="integration"?e.authConfig?.[n]?.integration??[]:e.authConfig?.[n]?.account??[];return new Set([...U[n][t],...r])}function
|
|
3
|
-
Schema: ${JSON.stringify(We(i),null,2)}`)}async function
|
|
1
|
+
import{a as ie,b as ot,c as P,d as B,e as it,f as z,g as U,h as se,i as Re,j as st,k as kn,l as at,m as ct}from"./chunk-6D4UDUPJ.js";import{a as rt}from"./chunk-IGGCNGU2.js";import*as mr from"querystring";var Fe=["ahrefs","airtable","amplitude","asana","bitwarden","bluesky","box","cal","calendly","cloudflare","cursor","discord","dodopayments","dropbox","exa","figma","firecrawl","fireflies","github","gitlab","gmail","googlecalendar","googledrive","googlesheets","grafana","hackernews","hubspot","intercom","jira","linear","monday","notion","onedrive","openweathermap","oura","outlook","pagerduty","posthog","razorpay","reddit","resend","sentry","sharepoint","slack","spotify","strava","stripe","tally","tavily","teams","telegram","todoist","trello","twilio","twitter","twitterapiio","typeform","vapi","xquik","youtube","zendesk","zohomail","zoom"],ut={ahrefs:"Ahrefs",airtable:"Airtable",amplitude:"Amplitude",asana:"Asana",bitwarden:"Bitwarden",bluesky:"Bluesky",box:"Box",cal:"Cal",calendly:"Calendly",cloudflare:"Cloudflare",cursor:"Cursor",discord:"Discord",dodopayments:"Dodo Payments",dropbox:"Dropbox",exa:"Exa",figma:"Figma",firecrawl:"Firecrawl",fireflies:"Fireflies",github:"GitHub",gitlab:"GitLab",gmail:"Gmail",googlecalendar:"Google Calendar",googledrive:"Google Drive",googlesheets:"Google Sheets",grafana:"Grafana",hackernews:"Hacker News",hubspot:"HubSpot",intercom:"Intercom",jira:"Jira",linear:"Linear",monday:"Monday",notion:"Notion",onedrive:"OneDrive",openweathermap:"OpenWeatherMap",oura:"Oura",outlook:"Outlook",pagerduty:"PagerDuty",posthog:"PostHog",razorpay:"Razorpay",reddit:"Reddit",resend:"Resend",sentry:"Sentry",sharepoint:"SharePoint",slack:"Slack",spotify:"Spotify",strava:"Strava",stripe:"Stripe",tally:"Tally",tavily:"Tavily",teams:"Teams",telegram:"Telegram",todoist:"Todoist",trello:"Trello",twilio:"Twilio",twitter:"Twitter",twitterapiio:"Twitter API IO",typeform:"Typeform",vapi:"Vapi",xquik:"XQuik",youtube:"YouTube",zendesk:"Zendesk",zohomail:"Zoho Mail",zoom:"Zoom"};function ve(e){let n=ut[e];return n||e.charAt(0).toUpperCase()+e.slice(1)}var Ie="https://auth.corsair.dev";var V=class extends Error{constructor(){super("Hub is not configured. Pass hub: { projectApiKey, signingSecret, deliveryUrl } to createCorsair()."),this.name="HubNotConfiguredError"}};function Be(e){let n=(e.apiUrl?.trim()||Ie).replace(/\/$/,""),t=e.projectApiKey.trim(),r=e.signingSecret.trim(),o=e.deliveryUrl.trim();if(!t||!r||!o)throw new Error("Hub config requires non-empty projectApiKey, signingSecret, and deliveryUrl");return{apiUrl:n,projectApiKey:t,signingSecret:r,deliveryUrl:o,oauthCallbackUrl:e.oauthCallbackUrl?.trim()}}function br(e){return e.apiUrl.trim().length>0&&e.deliveryUrl.trim().length>0&&e.projectApiKey.trim().length>0&&e.signingSecret.trim().length>0}function M(e){let n=P(e).hub;if(!n||!br(n))throw new V;return n}function Ue(e){return e.oauthCallbackUrl?e.oauthCallbackUrl:`${e.apiUrl.replace(/\/$/,"")}/oauth/callback`}function N(e){return typeof e=="string"&&e.length>0}function dt(e){if(!e||typeof e!="object")throw new Error("Hub API returned an empty connect session");let n=e;if(!N(n.connectUrl)||!N(n.token)||!N(n.projectId))throw new Error("Hub API returned an incomplete connect session (expected connectUrl, token, and projectId)");let t={connectUrl:n.connectUrl,token:n.token,projectId:n.projectId};return N(n.expiresAt)&&(t.expiresAt=n.expiresAt),t}function lt(e){if(!e||typeof e!="object")throw new Error("Hub API returned an empty permission session");let n=e;if(!N(n.approvalUrl)||!N(n.token)||!N(n.projectId)||!N(n.expiresAt))throw new Error("Hub API returned an incomplete permission session (expected approvalUrl, token, projectId, and expiresAt)");return{approvalUrl:n.approvalUrl,token:n.token,projectId:n.projectId,expiresAt:n.expiresAt}}function pt(e){if(!e||typeof e!="object")throw new Error("Hub token refresh returned an empty response");let n=e;if(!N(n.access_token))throw new Error("Hub token refresh returned no access_token");return{access_token:n.access_token,refresh_token:N(n.refresh_token)?n.refresh_token:void 0,expires_in:typeof n.expires_in=="number"?n.expires_in:void 0,scope:N(n.scope)?n.scope:void 0}}function gt(e){if(!e||typeof e!="object")return null;let n=e;return n.error??n.message??null}function wr(e){return e.replace(/\/$/,"")}async function Cr(e){let n=e.headers.get("content-type")??"",t=await e.text();if(!t)return null;if(!n.includes("application/json")&&t.trimStart().startsWith("<"))throw new Error(`Hub API returned HTML instead of JSON (HTTP ${e.status}). Check HUB_API_URL and deploy the latest hub API.`);try{return JSON.parse(t)}catch{throw new Error(`Hub API returned invalid JSON (HTTP ${e.status})`)}}function Tr(e,n,t){if(n===404&&t)return t;let r=gt(e);return r||`Hub API returned HTTP ${n}`}async function me(e){let n=wr(e.hub.apiUrl),t=await fetch(`${n}${e.path}`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.hub.projectApiKey}`},body:JSON.stringify(e.body)}),r=await Cr(t);if(!t.ok)throw new Error(Tr(r,t.status,e.notFoundMessage));return e.parseResponse(r)}import*as Se from"crypto";function J(e,n){return Buffer.from(JSON.stringify({plugin:e,tenantId:n,iat:Date.now()})).toString("base64url")}function bn(e,{maxAgeMs:n}={}){try{let t=e.includes(".")?e.split(".")[0]:e,r=JSON.parse(Buffer.from(t,"base64url").toString("utf-8"));if(r!==null&&typeof r=="object"&&"plugin"in r&&"tenantId"in r&&typeof r.plugin=="string"&&typeof r.tenantId=="string"){let o=r;return n!==void 0&&typeof o.iat=="number"&&Date.now()-o.iat>n?null:o}return null}catch{return null}}function ae(e,n){let t=Se.createHmac("sha256",n).update(e).digest("base64url");return`${e}.${t}`}var Pr=600*1e3;function Ne(e,n){let t=e.lastIndexOf(".");if(t===-1)return null;let r=e.slice(0,t),o=e.slice(t+1),i=Se.createHmac("sha256",n).update(r).digest("base64url"),s=Buffer.from(o,"base64url"),a=Buffer.from(i,"base64url");return s.length!==a.length||!Se.timingSafeEqual(s,a)?null:bn(r,{maxAgeMs:Pr})}var xr=new Set(["localhost","127.0.0.1","[::1]","::1"]);function _e(e){try{let{hostname:n,protocol:t}=new URL(e);return t!=="http:"&&t!=="https:"?!1:xr.has(n)}catch{return!1}}function ke(e){return _e(e)?"client":"server"}function ft(e,n){return e==="client"||_e(n)}function be(e){if(!e.source)return null;let n=_e(e.deliveryUrl);return e.source==="server"&&n?{error:'source "server" cannot be used with a loopback delivery URL \u2014 omit source to auto-detect or use "client"',status:400}:e.oauthMode==="managed"&&e.source==="client"&&!n?{error:'managed OAuth with source "client" requires a loopback delivery URL \u2014 omit source for production server delivery',status:400}:null}import{ZodBoolean as Rr,ZodDate as vr,ZodEnum as Ir,ZodNullable as Sr,ZodNumber as _r,ZodObject as Er,ZodOptional as Or,ZodRecord as Dr,ZodString as Hr,ZodType as Mr}from"zod";var Ar={slack:{channels:{list:{}},users:{list:{}}},linear:{projects:{list:{}},issues:{list:{}},users:{list:{}}},github:{issues:{list:{}},repositories:{list:{}}},discord:{guilds:{list:{}},channels:{list:{}}},hubspot:{contacts:{getMany:{}},companies:{getMany:{}},deals:{getMany:{}}},gmail:{messages:{list:{}},labels:{list:{}},drafts:{list:{}},threads:{list:{}}},googlecalendar:{events:{getMany:{}}},googledrive:{files:{list:{}},folders:{list:{}},sharedDrives:{list:{}}},notion:{databases:{getManyDatabases:{}},databasePages:{getManyDatabasePages:{}},users:{getManyUsers:{}}},airtable:{bases:{getMany:{}}},todoist:{projects:{getMany:{}},tasks:{getMany:{}}},cal:{bookings:{list:{}}},zohomail:{folders:{list:{}},messages:{list:{}}}},wn=Ar;async function ht(e,n){let t=[],r=g=>{t.push(g),console.log(g)},o=g=>{t.push(g),console.warn(g)},i=n?.caller??"script",s=ot(e);if(!s)throw new Error("setupCorsair: invalid corsair instance");if(!s.database)throw new Error("setupCorsair: a database must be configured on the corsair instance");let a=Fr(e,s.plugins,n),c={...s,database:s.database},d=c.database.db;await Nr(d,o);let l=await Wr(d,c,a.tenantId,a.provisionAccounts,r);n?.credentials&&Object.keys(n.credentials).length>0&&await Kr(e,a,n.credentials,c,r,o);let u=await qr(l,a,r,i);if(n?.backfill){r("[corsair:setup] Starting backfill...");let g=kt({plugins:s.plugins,database:d,kek:s.kek,multiTenancy:!0}).withTenant(a.tenantId);await Gr(g,s.plugins,u,r,o),r("[corsair:setup] Backfill complete.")}return t.join(`
|
|
2
|
+
`)}function Cn(e,n,t){let r=t==="integration"?e.authConfig?.[n]?.integration??[]:e.authConfig?.[n]?.account??[];return new Set([...U[n][t],...r])}function $r(e,n){if(!e)return!1;for(let[t,r]of Object.entries(e)){let o=n.find(a=>a.id===t);if(!o)continue;let i=se(o);if(!i)continue;let s=Cn(o,i,"account");for(let a of Object.keys(r))if(s.has(a))return!0}return!1}function Fr(e,n,t){let r=it(e),o=t?.tenantId?.trim(),i=o!==void 0&&o.length>0;if(r&&t?.backfill&&!i)throw new Error("setupCorsair: tenantId is required for backfill on a multi-tenant instance");if(r&&$r(t?.credentials,n)&&!i)throw new Error("setupCorsair: tenantId is required when setting account-level credentials on a multi-tenant instance");if(i&&!o)throw new Error("setupCorsair: tenantId must be a non-empty string");return{multiTenant:r,tenantIdProvided:i,tenantId:i?o:"default",provisionAccounts:!r||i}}function mt(e,n){return ie(e)?n===0?!0:Object.values(e).every(t=>mt(t,n-1)):!1}function Br(e){return mt(e,4)}var Ur={...at};function We(e){if(e instanceof Er){let n={};for(let[t,r]of Object.entries(e.shape))n[t]=r instanceof Mr?We(r):"unknown";return n}return e instanceof Sr?`${We(e.unwrap())} | null`:e instanceof Or?`${We(e.unwrap())} | undefined`:e instanceof Ir?e.options.join(" | "):e instanceof Hr?"string":e instanceof _r?"number":e instanceof Rr?"boolean":e instanceof vr?"date":e instanceof Dr?"jsonb":"unknown"}async function Nr(e,n){let t=await e.introspection.getTables(),r=new Set(t.map(o=>o.name));for(let[o,i]of Object.entries(Ur))r.has(o)||n(`[corsair:setup] Table "${o}" does not exist. Run your database migrations before calling setupCorsair.
|
|
3
|
+
Schema: ${JSON.stringify(We(i),null,2)}`)}async function Wr(e,n,t,r,o){let i=new Date,s=new Map;for(let a of n.plugins){let c=a.id,d=se(a),l=await e.selectFrom("corsair_integrations").selectAll().where("name","=",c).executeTakeFirst();if(!l){let f=crypto.randomUUID();await e.insertInto("corsair_integrations").values({id:f,name:c,config:{},created_at:i,updated_at:i}).execute(),l=await e.selectFrom("corsair_integrations").selectAll().where("id","=",f).executeTakeFirst(),o(`[corsair:setup] Created integration: ${c}`)}let u=d?a.authConfig?.[d]?.integration??[]:[],g=d?a.authConfig?.[d]?.account??[]:[],y=d&&l?D({authType:d,integrationName:c,kek:n.kek,database:n.database,extraIntegrationFields:u}):void 0;if(l&&!l.dek&&y&&(await y.issue_new_dek(),o(`[corsair:setup] Issued integration DEK: ${c}`)),!l||!d||!y)continue;let p;if(r){let f=await e.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",t).where("integration_id","=",l.id).executeTakeFirst();if(!f){let h=crypto.randomUUID();await e.insertInto("corsair_accounts").values({id:h,tenant_id:t,integration_id:l.id,config:{},created_at:i,updated_at:i}).execute(),f=await e.selectFrom("corsair_accounts").selectAll().where("id","=",h).executeTakeFirst(),o(`[corsair:setup] Created account: ${c}`)}p=f&&I({authType:d,integrationName:c,tenantId:t,kek:n.kek,database:n.database,extraAccountFields:g}),f&&p&&!f.dek&&(await p.issue_new_dek(),o(`[corsair:setup] Issued account DEK: ${c}`))}s.set(c,{pluginId:c,authType:d,integration:y,account:p,integrationFields:[...U[d].integration,...u],accountFields:r?[...U[d].account,...g]:[]})}return s}function Lr(e){if(!ie(e))return;let n=e.keys;return ie(n)?n:void 0}function jr(e,n){return"withTenant"in e&&typeof e.withTenant=="function"?e.withTenant(n):e}async function Kr(e,n,t,r,o,i){let s=Lr(e),a=n.provisionAccounts?jr(e,n.tenantId):void 0;for(let[c,d]of Object.entries(t)){let l=r.plugins.find(m=>m.id===c);if(!l){i(`[corsair:setup] Unknown plugin '${c}' \u2014 skipping credentials.`);continue}let u=se(l);if(!u){i(`[corsair:setup] Plugin '${c}' has no auth type \u2014 skipping credentials.`);continue}let g=Cn(l,u,"integration"),y=Cn(l,u,"account"),p=s?.[c],f=a?.[c],h=ie(f)?f.keys:void 0;for(let[m,C]of Object.entries(d))if(C){if(g.has(m)){if(n.multiTenant&&n.tenantIdProvided)throw new Error(`[corsair:setup] '${c}.${m}' is an integration-level credential shared across all tenants. You passed tenantId="${n.tenantId}", which only scopes account-level credentials. Run setup without --tenant if you intend to change this credential globally.`);let b=z(p,`set_${m}`);if(!b){i(`[corsair:setup] Cannot set integration field '${m}' for '${c}'.`);continue}await b(C),o(`[corsair:setup] Set ${c} integration.${m}`);continue}if(y.has(m)){if(n.multiTenant&&!n.tenantIdProvided)throw new Error(`setupCorsair: tenantId is required to set account-level credential '${c}.${m}' on a multi-tenant instance`);let b=z(h,`set_${m}`);if(!b){i(`[corsair:setup] Cannot set account field '${m}' for '${c}'.`);continue}await b(C),o(`[corsair:setup] Set ${c} account.${m} (tenant=${n.tenantId})`);continue}i(`[corsair:setup] Unknown credential field '${m}' for plugin '${c}'.`)}}}var yt=new Set(["webhook_signature","expires_at","scope","redirect_url"]);async function Zr(e,n,t,r,o,i,s,a,c){let d=[],l=[];for(let g of o){if(yt.has(g))continue;let y=z(t,`get_${g}`);if(!y)continue;let p=null;try{let f=await y();p=typeof f=="string"?f:null}catch{}p||d.push(g)}if(r&&i.length>0)for(let g of i){if(yt.has(g))continue;let y=z(r,`get_${g}`);if(!y)continue;let p=null;try{let f=await y();p=typeof f=="string"?f:null}catch{}p||l.push(g)}let u=d.length===0&&l.length===0;if(u)a(`[corsair:setup] '${e}' (${n}) is configured \u2713`);else{let g=[...d,...l];if(c==="cli"){let y=g.map(p=>`${p}=VALUE`).join(" ");a(`[corsair:setup] '${e}' (${n}) needs credentials. Run:
|
|
4
4
|
corsair setup --${e} ${y}`)}else{let y=[`[corsair:setup] '${e}' (${n}) needs credentials. Call:`];for(let p of d)y.push(` await corsair.keys.${e}.set_${p}(value)`);for(let p of l){let f=s.provisionAccounts?s.tenantId==="default"?`corsair.${e}`:`corsair.withTenant(${JSON.stringify(s.tenantId)}).${e}`:`corsair.withTenant(<tenant>).${e}`;y.push(` await ${f}.keys.set_${p}(value)`)}a(y.join(`
|
|
5
|
-
`))}}return u}async function Zr(e,n,t,r){let o=new Set;for(let i of e.values())await Kr(i.pluginId,i.authType,i.integration,i.account,i.integrationFields,i.accountFields,n,t,r)&&o.add(i.pluginId);return o}async function qr(e,n,t,r,o){if(!Fr(wn)){o("[corsair:setup] Backfill config is invalid - skipping backfill.");return}let i=wn,s=new Set(n.map(a=>a.id));for(let[a,c]of Object.entries(i)){if(!s.has(a))continue;if(!t.has(a)){r(`[corsair:setup] Skipping backfill for '${a}' \u2014 auth not configured.`);continue}let d=ie(e)?e[a]:void 0,l=ie(d)?d.api:void 0;if(l)for(let[u,g]of Object.entries(c))for(let[y,p]of Object.entries(g)){r(`[corsair:setup] Backfilling ${a} \u203A ${u}.${y}...`);try{let f=ie(l)?l[u]:void 0;await z(f,y)?.(p)}catch(f){o(`[corsair:setup] ${a} \u203A ${u}.${y} failed: `+(f instanceof Error?f.message:String(f)))}}}}async function we(e,n){if(!P(e).database)throw new Error("A database must be configured to provision Corsair for connect");await ht(e,{tenantId:n})}var bt=new Set(["webhook_signature","expires_at","scope"]);function Gr(e){return e==="oauth_2"||e==="managed"?"oauth":e==="bot_token"?"bot_token":"api_key"}async function zr(e,n,t,r){let o=Re(n,t).filter(c=>!bt.has(c));if(o.length===0)return!0;let s=(typeof e.withTenant=="function"?e.withTenant(r):e)[n.id],a=s&&typeof s=="object"&&"keys"in s?s.keys:null;if(!a)return!1;for(let c of o){if((t==="oauth_2"||t==="managed")&&c==="refresh_token")continue;let d=z(a,`get_${c}`);if(!d)continue;let l=null;try{let u=await d();l=typeof u=="string"?u:null}catch{l=null}if(!l)return!1}return!0}function Jr(e,n){return e==="oauth_2"||e==="managed"?[]:n.filter(t=>!bt.has(t))}async function wt(e,n,t={}){let r=P(e),o=M(e),i=Ue(o),s=[],a=t.pluginIds?new Set(t.pluginIds):null;for(let c of r.plugins){if(a&&!a.has(c.id))continue;let d=se(c);if(!d)continue;let l=Gr(d),u=ve(c.id),g=await zr(e,c,d,n),y=Re(c,d),p={plugin:c.id,providerName:u,authKind:l,alreadyConfigured:g};if(l==="oauth"){let h=t.oauthModeOverrides?.[c.id]??(d==="managed"?"managed":"byo");if(p.oauthMode=h,!g)if(h==="managed")p.state=ae(J(c.id,n),r.kek);else try{let m=await Tn(e,c.id,{tenantId:n,redirectUri:i});p.oauthUrl=m.url,p.state=m.state}catch{continue}}else{let f=Jr(d,y);f.length>0&&(p.credentialFields=f)}s.push(p)}return s}function Ct(e,n){let t=M(e);if(n){let r=be({source:n,deliveryUrl:t.deliveryUrl});if(r)throw new Error(r.error);return n}return ke(t.deliveryUrl)}async function Tt(e,n){await we(e,n)}async function Le(e,n){let t=M(e);await Tt(e,n.tenantId);let r=n.plugin?[n.plugin]:void 0,o=n.plugin&&n.oauthMode?{[n.plugin]:n.oauthMode}:void 0,i=await wt(e,n.tenantId,{pluginIds:r,oauthModeOverrides:o});if(i.length===0)throw new Error(n.plugin?`Plugin '${n.plugin}' is not configured on this Corsair instance`:"No plugins are configured on this Corsair instance");if(i.filter(c=>!c.alreadyConfigured).length===0)throw new Error(n.plugin?`Plugin '${n.plugin}' is already configured for this tenant`:"All plugins are already configured for this tenant");let a=Ct(e,n.source);return me({hub:t,path:"/connect/sessions",notFoundMessage:"Hub REST API not found at /connect/sessions. Check HUB_API_URL and ensure the Hub API is deployed.",body:{tenantId:n.tenantId,deliveryUrl:t.deliveryUrl,source:a,plugins:i},parseResponse:dt})}function Vr(e){if(e==="byo"||e==="managed")return e}function Yr(e){if(e==="client"||e==="server")return e}function Pn(e){let n=e.plugin?.trim(),t=e.tenantId?.trim()||"default",r=Yr(e.source),o=Vr(e.oauthMode),i=e.providerName?.trim();if(e.source!==void 0&&e.source!==null&&String(e.source).trim()!==""&&!r)return{error:'source must be "client" or "server"',status:400};if(e.oauthMode&&!o)return{error:'oauthMode must be "byo" or "managed"',status:400};let s={tenantId:t,oauthMode:o};return n&&(s.plugin=n),r&&(s.source=r),i&&(s.providerName=i),s}function xn(e){let{searchParams:n}=new URL(e),t=n.get("oauthMode");return{plugin:n.get("plugin")??void 0,tenantId:n.get("tenantId")??void 0,source:n.get("source")??void 0,oauthMode:t??void 0,providerName:n.get("providerName")??void 0}}function Qr(e){return{ok:!0,connectUrl:e.connectUrl,token:e.token,projectId:e.projectId,expiresAt:e.expiresAt}}async function Xr(e,n,t){if(t?.resolveTenantId){let r=await t.resolveTenantId(e);return r?.trim()?r.trim():{error:"Unauthorized",status:401}}return n.tenantId?.trim()||t?.defaultTenantId?.trim()||"default"}async function An(e,n,t){let o=n.method.toUpperCase()==="GET"?xn(n.url):await n.json().catch(()=>null);if(!o)return{error:"Invalid JSON body",status:400};let i=await Xr(n,o,t);if(typeof i!="string")return i;let s=Pn({...o,tenantId:i});if("error"in s)return s;let a=M(e);if(s.source){let l=be({source:s.source,deliveryUrl:a.deliveryUrl,oauthMode:s.oauthMode});if(l)return l}let c={...s,source:s.source??ke(a.deliveryUrl)},d=await Le(e,c);return Qr(d)}async function Rn(e,n,t){try{let r=await An(e,n,t);return"error"in r?Response.json({error:r.error},{status:r.status}):Response.json(r)}catch(r){if(r instanceof V)return Response.json({error:r.message},{status:503});let o=r instanceof Error?r.message:String(r);return Response.json({error:o},{status:500})}}async function je(e,n,t){let r=n.method.toUpperCase();return r!=="GET"&&r!=="POST"?Response.json({error:"Method not allowed"},{status:405}):Rn(e,n,t)}var Y=class extends Error{code;constructor(n,t){super(t),this.name="AuthCredentialsDeliveryError",this.code=n}};async function Pt(e,n){let t=P(e,()=>new Y("invalid_corsair_instance","Invalid corsair instance"));if(!t.database)throw new Y("no_database","Database not configured");let r=B(t,n.plugin,d=>new Y("plugin_not_found",d)),o=se(r);if(!o)throw new Y("invalid_credentials",`Plugin '${r.id}' has no authType configured`);if(o==="oauth_2"||o==="managed")throw new Y("invalid_credentials","OAuth plugins must be connected via sign-in, not credentials delivery");await we(e,n.tenantId);let i=new Set(Re(r,o)),a=r.authConfig?.[o]?.account??[],c=I({authType:o,integrationName:n.plugin,tenantId:n.tenantId,kek:t.kek,database:t.database,extraAccountFields:a});for(let[d,l]of Object.entries(n.credentials)){if(!l.trim())continue;if(!i.has(d))throw new Y("invalid_credentials",`Unknown credential field '${d}' for plugin '${n.plugin}'`);let u=z(c,`set_${d}`);if(!u)throw new Y("invalid_credentials",`Cannot set credential field '${d}' for plugin '${n.plugin}'`);await u(l.trim())}return{plugin:n.plugin,tenantId:n.tenantId}}async function Ke(e,n,t){for(let r of e){if(r.id!==n)continue;let o=r.oauthWebhookTenantLinkResolver;return o?o(t):null}return null}import{createCipheriv as xt,createDecipheriv as At,randomBytes as Ze,scrypt as eo}from"crypto";import{promisify as no}from"util";var Rt=no(eo),qe="aes-256-gcm",vt=12,Ge=16,to=16,Ee=32;function Q(){return Ze(Ee).toString("base64")}async function X(e,n){let t=Ze(to),r=await Rt(n,t,Ee),o=Ze(vt),i=xt(qe,r,o,{authTagLength:Ge}),s=Buffer.concat([i.update(e,"utf8"),i.final()]),a=i.getAuthTag();return[t.toString("base64"),o.toString("base64"),a.toString("base64"),s.toString("base64")].join(":")}async function W(e,n){let[t,r,o,i]=e.split(":");if(!t||!r||!o||!i)throw new Error("Invalid encrypted DEK format");let s=Buffer.from(t,"base64"),a=Buffer.from(r,"base64"),c=Buffer.from(o,"base64"),d=Buffer.from(i,"base64"),l=await Rt(n,s,Ee),u=At(qe,l,a,{authTagLength:Ge});return u.setAuthTag(c),Buffer.concat([u.update(d),u.final()]).toString("utf8")}function vn(e,n){let t=Buffer.from(n,"base64"),r=Ze(vt),o=xt(qe,t,r,{authTagLength:Ge}),i=Buffer.concat([o.update(e,"utf8"),o.final()]),s=o.getAuthTag();return[r.toString("base64"),s.toString("base64"),i.toString("base64")].join(":")}function Oe(e,n){let[t,r,o]=e.split(":");if(!t||!r||!o)throw new Error("Invalid encrypted data format");let i=Buffer.from(n,"base64"),s=Buffer.from(t,"base64"),a=Buffer.from(r,"base64"),c=Buffer.from(o,"base64"),d=At(qe,i,s,{authTagLength:Ge});return d.setAuthTag(a),Buffer.concat([d.update(c),d.final()]).toString("utf8")}function ce(e,n){let t={};for(let[r,o]of Object.entries(e))t[r]=vn(o,n);return t}function ee(e,n){let t={};for(let[r,o]of Object.entries(e))t[r]=Oe(o,n);return t}function De(e,n,t){let r=ee(e,n);return ce(r,t)}function It(e,n,t){let r={};for(let o of t)r[`get_${o}`]=async()=>(await e())[o]??null,r[`set_${o}`]=async i=>{let s=[null,void 0,""].includes(i)?null:i;await n({[o]:s})};return r}var In=e=>{if(!e)return{};if(typeof e=="string")try{return JSON.parse(e)}catch{return{}}return e};function D(e){let{authType:n,integrationName:t,kek:r,database:o,extraIntegrationFields:i=[]}=e,s=[...U[n].integration,...i],a=null,c={kek:r,integrationName:t,getIntegration:async()=>{if(a)return a;let p=await o.db.selectFrom("corsair_integrations").selectAll().where("name","=",t).executeTakeFirst();if(!p)throw new Error(`Integration "${t}" not found. Make sure to create the integration first.`);return a={id:p.id,config:In(p.config),dek:p.dek??null},a},updateIntegration:async p=>{let f=await c.getIntegration();await o.db.updateTable("corsair_integrations").set({...p.config!==void 0?{config:p.config}:{},...p.dek!==void 0?{dek:p.dek}:{},updated_at:new Date}).where("id","=",f.id).execute(),a=null}},d=null,l=async()=>{if(d)return d;let p=await c.getIntegration();if(!p.dek)throw new Error(`No DEK found for integration "${t}". Initialize the integration first.`);return d=await W(p.dek,r),d},u=async()=>{let p=await c.getIntegration(),f=await l(),h=p.config;return!h||Object.keys(h).length===0?{}:ee(h,f)};return{get_dek:l,issue_new_dek:async()=>{let p=await c.getIntegration(),f=Q(),h={};if(p.dek){let C=await W(p.dek,r),b=p.config;b&&Object.keys(b).length>0&&(h=De(b,C,f))}let m=await X(f,r);return await c.updateIntegration({config:h,dek:m}),d=f,f},...It(u,async p=>{let f=await l(),h;try{h=await u()}catch(b){console.error(`[corsair] Failed to decrypt config for integration "${t}", starting fresh:`,b),h={}}let m={...h};for(let[b,k]of Object.entries(p))k===null?delete m[b]:m[b]=k;let C=ce(m,f);await c.updateIntegration({config:C})},s)}}function I(e){let{authType:n,integrationName:t,tenantId:r,kek:o,database:i,extraAccountFields:s=[]}=e,a=[...U[n].account,...s],c=null,d=null,l=async()=>{if(d)return d;let k=await i.db.selectFrom("corsair_integrations").selectAll().where("name","=",t).executeTakeFirst();if(!k)throw new Error(`Integration "${t}" not found. Make sure to create the integration first.`);return d={id:k.id,config:In(k.config),dek:k.dek??null},d},u={kek:o,integrationName:t,tenantId:r,getIntegration:l,getAccount:async()=>{if(c)return c;let k=await l(),w=await i.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",r).where("integration_id","=",k.id).executeTakeFirst();if(!w)throw new Error(`Account not found for tenant "${r}" and integration "${t}". Make sure to create the account first.`);return c={id:w.id,config:In(w.config),dek:w.dek??null},c},updateAccount:async k=>{let w=await u.getAccount();await i.db.updateTable("corsair_accounts").set({...k.config!==void 0?{config:k.config}:{},...k.dek!==void 0?{dek:k.dek}:{},updated_at:new Date}).where("id","=",w.id).execute(),c=null}},g=null,y=null,p=async()=>{if(g)return g;let k=await u.getAccount();if(!k.dek)throw new Error(`No DEK found for account (tenant: "${r}", integration: "${t}"). Initialize the account first.`);return g=await W(k.dek,o),g},f=async()=>{if(y)return y;let k=await u.getIntegration();if(!k.dek)throw new Error(`No DEK found for integration "${t}". Initialize the integration first.`);return y=await W(k.dek,o),y},h=async()=>{let k=await u.getAccount(),w=await p(),T=k.config;return!T||Object.keys(T).length===0?{}:ee(T,w)},m=async()=>{let k=await u.getIntegration(),w=await f(),T=k.config;return!T||Object.keys(T).length===0?{}:ee(T,w)},b={get_dek:p,issue_new_dek:async()=>{let k=await u.getAccount(),w=Q(),T={};if(k.dek){let _=await W(k.dek,o),E=k.config;E&&Object.keys(E).length>0&&(T=De(E,_,w))}let S=await X(w,o);return await u.updateAccount({config:T,dek:S}),g=w,w},...It(h,async k=>{let w=await p(),T;try{T=await h()}catch(E){console.error(`[corsair] Failed to decrypt config for account (tenant: "${r}", integration: "${t}"), starting fresh:`,E),T={}}let S={...T};for(let[E,x]of Object.entries(k))x===null?delete S[E]:S[E]=x;let _=ce(S,w);await u.updateAccount({config:_})},a)};return n==="oauth_2"&&(b.get_integration_credentials=async()=>{let k=await m();return{client_id:k.client_id||null,client_secret:k.client_secret||null,redirect_url:k.redirect_url??null}}),b}async function St(e,n,t){let r=await e.db.selectFrom("corsair_integrations").selectAll().where("name","=",n).executeTakeFirst();if(!r)throw new Error(`Integration "${n}" not found.`);let o=Q(),i=await X(o,t);return await e.db.updateTable("corsair_integrations").set({dek:i,updated_at:new Date}).where("id","=",r.id).execute(),o}async function _t(e,n,t,r){let o=await e.db.selectFrom("corsair_integrations").selectAll().where("name","=",n).executeTakeFirst();if(!o)throw new Error(`Integration "${n}" not found.`);let i=await e.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",t).where("integration_id","=",o.id).executeTakeFirst();if(!i)throw new Error(`Account not found for tenant "${t}" and integration "${n}".`);let s=Q(),a=await X(s,r);return await e.db.updateTable("corsair_accounts").set({dek:a,updated_at:new Date}).where("id","=",i.id).execute(),s}var Et=e=>{if(!e)return{};if(typeof e=="string")try{return JSON.parse(e)}catch{return{}}return e};async function ro(e){let n=await e.database.db.selectFrom("corsair_integrations").selectAll().where("name","=",e.pluginId).executeTakeFirst();if(!n)throw new Error(`Integration '${e.pluginId}' not found. Run setupCorsair before registering webhook tenant links.`);let t=await e.database.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",e.tenantId).where("integration_id","=",n.id).executeTakeFirst();if(!t)throw new Error(`Account not found for tenant '${e.tenantId}' and integration '${e.pluginId}'.`);return{integrationId:n.id,accountId:t.id}}async function oo(e){let n=await e.database.db.selectFrom("corsair_accounts").selectAll().where("id","=",e.accountId).executeTakeFirst();if(!n?.dek)throw new Error(`Account '${e.accountId}' has no DEK.`);let t=await W(n.dek,e.kek),r=Et(n.config),o={};Object.keys(r).length>0&&(o=ee(r,t)),o[e.link.linkType]=e.link.externalId;let i=ce(o,t);await e.database.db.updateTable("corsair_accounts").set({config:i,updated_at:new Date}).where("id","=",n.id).execute()}async function Ce(e){let{database:n,kek:t,pluginId:r,tenantId:o,link:i,authType:s,extraAccountFields:a=[]}=e,{accountId:c}=await ro({database:n,pluginId:r,tenantId:o}),d=!1;if(s){let l=I({authType:s,integrationName:r,tenantId:o,kek:t,database:n,extraAccountFields:a}),u=`set_${i.linkType}`,g=l[u];typeof g=="function"&&(await g(i.externalId),d=!0)}d||await oo({database:n,kek:t,accountId:c,link:i})}async function Sn(e){let{database:n,kek:t,pluginId:r,linkType:o,externalId:i}=e,s=await n.db.selectFrom("corsair_accounts as accounts").innerJoin("corsair_integrations as integrations","integrations.id","accounts.integration_id").selectAll("accounts").where("integrations.name","=",r).execute();for(let a of s)if(a.dek)try{let c=await W(a.dek,t),l=Et(a.config)[o];if(!l)continue;if(Oe(l,c)===i)return a}catch{continue}return null}async function ze(e){return(await Sn(e))?.tenant_id??null}async function Ot(e){return ze({database:e.database,kek:e.kek,pluginId:e.pluginId,linkType:e.match.linkType,externalId:e.match.externalId})}var ue=class extends Error{code;constructor(n,t){super(t),this.name="ManagedOAuthDeliveryError",this.code=n}};async function Te(e,n){let{plugin:t,tenantId:r,accessToken:o,refreshToken:i,expiresIn:s,scope:a}=n;if(!o.trim())throw new ue("no_access_token","Managed OAuth delivery missing access_token");let c=P(e,()=>new ue("invalid_corsair_instance","Invalid corsair instance"));if(!c.database)throw new ue("no_database","No database configured on corsair instance");let d=B(c,t,u=>new ue("plugin_not_found",u));await we(e,r);let l=I({authType:"managed",integrationName:t,tenantId:r,kek:c.kek,database:c.database});await l.set_access_token(o),i&&await l.set_refresh_token(i),s&&await l.set_expires_at(String(Math.floor(Date.now()/1e3)+s)),a&&await l.set_scope(a);try{let u=await Ke(c.plugins,t,{access_token:o,refresh_token:i,scope:a});if(u)try{let g=d.authConfig?.managed?.account??[];await Ce({database:c.database,kek:c.kek,pluginId:t,tenantId:r,link:u,authType:"managed",extraAccountFields:g})}catch(g){console.warn(`[corsair:managed-oauth] Failed to persist webhook tenant link for '${t}' tenant '${r}':`,g)}}catch(u){console.warn(`[corsair:managed-oauth] Failed to resolve webhook tenant link for '${t}' tenant '${r}':`,u)}return{plugin:t,tenantId:r}}function io(e){return e!==null&&typeof e=="object"&&"match"in e&&"handler"in e&&typeof e.match=="function"&&typeof e.handler=="function"}function Dt(e,n,t=[]){for(let[r,o]of Object.entries(e))if(io(o)){if(o.match(n))return{webhook:o,path:[...t,r]}}else if(o&&typeof o=="object"){let i=Dt(o,n,[...t,r]);if(i)return i}return null}function so(e){let n={};for(let[t,r]of Object.entries(e))n[t.toLowerCase()]=Array.isArray(r)?r[0]:r;return n}function ao(e){let n=e["x-goog-resource-uri"],t=e["x-goog-channel-id"];if(!n||!t)return null;let r={resourceId:e["x-goog-resource-id"]||"",resourceState:e["x-goog-resource-state"]||"",resourceUri:n,channelId:t,channelExpiration:e["x-goog-channel-expiration"]||""};return n.includes("/drive/")&&(r.kind="drive#change"),{message:{data:Buffer.from(JSON.stringify(r)).toString("base64"),messageId:e["x-goog-message-number"]||""}}}async function Ht(e,n,t,r){let o=so(n),i=typeof t=="string"?JSON.parse(t):t;(!i||typeof i=="object"&&Object.keys(i).length===0)&&o["x-goog-resource-uri"]&&(i=ao(o)||i);let a={headers:o,body:i,...r?{query:r}:{}},c=r?.tenantId||"default",d=e.withTenant?e.withTenant(c):e,l=Fe;for(let u of l){let g=d[u];if(!g||!g.webhooks||g.pluginWebhookMatcher&&!g.pluginWebhookMatcher(a))continue;let y=Dt(g.webhooks,a);if(!y)continue;let p=y.path.join("."),f={payload:i,headers:o,rawBody:typeof t=="string"?t:JSON.stringify(t),...r?{query:r}:{}};try{let h=await y.webhook.handler(f),m=!!Object.keys(h.returnToSender||{})?.length;return{plugin:u,action:p,body:i,response:m?{...h?.returnToSender,success:!0}:{success:!0},...h.responseHeaders&&{responseHeaders:h.responseHeaders}}}catch(h){return console.error(`Error executing webhook handler for ${u}.${p}:`,h),{plugin:u,action:p,body:i,response:{success:!1,error:h instanceof Error?h.message:"Unknown error"}}}}return{plugin:null,action:null,body:null}}import{createHmac as co,timingSafeEqual as uo}from"crypto";var lo=300*1e3;function po(e){if(e)return e.startsWith("sha256=")?e.slice(7):e}function _n(e){let n=e.signingSecret.trim();if(!n)return{ok:!1,error:"Tunnel signing secret is required"};let t=po(e.signatureHeader);if(!t)return{ok:!1,error:"Invalid tunnel signature"};let r=Number(e.timestampHeader);if(!Number.isFinite(r))return{ok:!1,error:"Invalid or missing tunnel timestamp"};if(Math.abs(Date.now()-r*1e3)>lo)return{ok:!1,error:"Tunnel request timestamp is outside the allowed window"};let i=co("sha256",n).update(e.body).digest("hex");try{if(!uo(Buffer.from(i,"utf8"),Buffer.from(t,"utf8")))return{ok:!1,error:"Invalid tunnel signature"}}catch{return{ok:!1,error:"Invalid tunnel signature"}}return{ok:!0}}import{createHmac as go,timingSafeEqual as fo}from"crypto";function Je(e){return e.deliveryMode==="permission.approve"||e.deliveryMode==="permission.deny"}function Ve(e){return e.deliveryMode==="oauth.tokens"||typeof e.accessToken=="string"&&e.accessToken.length>0}function Mt(e){let n=e.trim();return n.length>0?n:null}function yo(e,n){let t=Mt(n);if(!t)throw new Error("Signing secret is required for browser delivery tokens");return go("sha256",t).update(e).digest("base64url")}function Ye(e,n){let t=Mt(n);if(!t)return null;let r=e.split(".");if(r.length!==2)return null;let[o,i]=r;if(!o||!i)return null;let s=yo(o,t);try{if(!fo(Buffer.from(i,"utf8"),Buffer.from(s,"utf8")))return null}catch{return null}let a;try{a=JSON.parse(Buffer.from(o,"base64url").toString("utf8"))}catch{return null}return a.exp*1e3<Date.now()?null:a}function $t(e,n){if(e instanceof Headers)return e.get(n)??void 0;let t=e[n]??e[n.toLowerCase()];return Array.isArray(t)?t[0]:typeof t=="string"?t:void 0}async function ho(e,n,t){let r=typeof t.tenantId=="string"?t.tenantId:typeof t.query?.tenantId=="string"?t.query.tenantId:void 0;return r||(!n.database||!t.plugin||!t.linkType||!t.externalId?void 0:await ze({database:n.database,kek:n.kek,pluginId:t.plugin,linkType:t.linkType,externalId:t.externalId})??void 0)}async function mo(e,n,t){let r=await ho(e,n,t),o={...t.query??{},...r?{tenantId:r}:{}},i=await Ht(e,t.headers,t.body,o);if(!i.plugin)return{status:"failed",retryable:!1,error:"No matching webhook handler found"};if(i.response&&i.response.success===!1)return{status:"failed",retryable:!1,error:typeof i.response.error=="string"?i.response.error:"Webhook handler failed"};let s=i.response?.returnToSender,a=s&&typeof s=="object"&&typeof s.validationToken=="string"&&Object.keys(s).length===1?s.validationToken:s||(i.response?.data??i.response);return{status:"ok",webhookResponse:{status:i.response?.statusCode??200,body:a,headers:i.responseHeaders}}}async function ko(e,n){return await He(e,n),{status:"ok"}}async function bo(e,n){return await Te(e,{plugin:n.plugin,tenantId:n.tenantId,accessToken:n.accessToken,refreshToken:n.refreshToken,expiresIn:n.expiresIn,scope:n.scope}),{status:"ok"}}async function On(e,n,t){let r=await En(e,{token:n},t);if(r.status!=="ok")throw new Error(r.error??"Permission decision failed")}async function En(e,n,t){let r=P(e),o=n.token?.trim();if(!o)return{status:"failed",retryable:!1,error:"Permission token is required"};if(!r.database)return{status:"failed",retryable:!1,error:"Database not configured"};let i=new Date().toISOString(),s=await r.database.db.selectFrom("corsair_permissions").selectAll().where("token","=",o).executeTakeFirst();return s?s.status!=="pending"?{status:"ok"}:s.expires_at<i?(await r.database.db.updateTable("corsair_permissions").set({status:"expired",updated_at:new Date}).where("id","=",s.id).execute(),{status:"failed",retryable:!1,error:"Permission has expired"}):(await r.database.db.updateTable("corsair_permissions").set({status:t,updated_at:new Date}).where("id","=",s.id).execute(),{status:"ok"}):{status:"failed",retryable:!1,error:"Permission not found"}}async function wo(e,n){try{return await Pt(e,n),{status:"ok"}}catch(t){return{status:"failed",retryable:!1,error:t instanceof Error?t.message:"Credential delivery failed"}}}async function Dn(e,n,t={}){let r=P(e),o=$t(n.headers,"x-corsair-signature"),i=$t(n.headers,"x-corsair-timestamp");if(t.signingSecret?.trim()){let a=_n({body:n.body,signatureHeader:o,timestampHeader:i,signingSecret:t.signingSecret});if(!a.ok)return{status:"failed",retryable:!1,error:a.error}}else if(!t.allowUnsignedTunnel)return{status:"failed",retryable:!1,error:"Tunnel signing secret is required"};let s;try{s=JSON.parse(n.body)}catch{return{status:"failed",retryable:!1,error:"Invalid tunnel envelope JSON"}}switch(s.type){case"webhook":return mo(e,r,s.payload);case"oauth.callback":return ko(e,s.payload);case"oauth.tokens":return bo(e,s.payload);case"permission.approve":return En(e,s.payload,"approved");case"permission.deny":return En(e,s.payload,"denied");case"auth.credentials":return wo(e,s.payload);default:return{status:"failed",retryable:!1,error:`Unsupported tunnel type: ${s.type}`}}}async function Hn(e,n){let t=M(e),o=new URL(n).searchParams.get("d");if(!o)return{type:"json",status:200,body:{status:"ok",message:"Corsair tunnel endpoint is active",timestamp:new Date().toISOString()}};let i=Ye(o,t.signingSecret);if(!i)return{type:"json",status:400,body:{error:"Invalid or expired delivery token"}};try{if(Je(i)){if(!i.permissionToken)return{type:"json",status:400,body:{error:"Permission delivery missing permission token"}};await On(e,i.permissionToken,i.deliveryMode==="permission.approve"?"approved":"denied")}else if(Ve(i)){if(!i.accessToken)return{type:"json",status:400,body:{error:"Managed OAuth delivery missing access_token"}};await Te(e,{plugin:i.plugin,tenantId:i.tenantId,accessToken:i.accessToken,refreshToken:i.refreshToken,expiresIn:i.expiresIn,scope:i.scope})}else{if(!i.code||!i.state||!i.redirectUri)return{type:"json",status:400,body:{error:"Invalid BYO OAuth delivery token"}};await He(e,{code:i.code,state:i.state,redirectUri:i.redirectUri})}}catch(s){return{type:"json",status:400,body:{error:s instanceof Error?s.message:"Hub delivery failed"}}}return{type:"redirect",url:i.hubSuccessUrl}}async function Mn(e,n){let t=M(e),r=await Dn(e,n,{signingSecret:t.signingSecret});if(r.status!=="ok")return{type:"json",status:r.retryable===!1?400:502,body:{error:r.error??"Tunnel processing failed"}};let o=r.webhookResponse;if(!o)return{type:"json",status:200,body:{status:"ok"}};let i=o.status??200,s=o.headers;return o.body&&typeof o.body=="object"&&!(o.body instanceof ArrayBuffer)?{type:"json",status:i,body:o.body,headers:s}:{type:"text",status:i,body:typeof o.body=="string"?o.body:o.body?JSON.stringify(o.body):null,headers:s}}function $n(e){if(e.type==="redirect")return Response.redirect(e.url,302);let n=new Headers;for(let[t,r]of Object.entries(e.headers??{}))typeof r=="string"&&n.set(t,r);return e.type==="json"?Response.json(e.body,{status:e.status,headers:n}):new Response(e.body,{status:e.status,headers:n})}async function Fn(e,n){return n.method==="GET"?Hn(e,n.url):Mn(e,{headers:n.headers,body:n.body??""})}async function Bn(e,n){try{let t=await Fn(e,n);return $n(t)}catch(t){if(t instanceof V)return Response.json({error:t.message},{status:503});throw t}}async function Qe(e,n){let t=n.method.toUpperCase();return t!=="GET"&&t!=="POST"?Response.json({error:"Method not allowed"},{status:405}):Bn(e,{method:t,url:n.url,headers:n.headers,body:t==="POST"?await n.text():void 0})}var ne=class extends Error{pluginId;authType;constructor(n,t,r){super(r??`[auth-missing:${n}:${t}]`),Object.setPrototypeOf(this,new.target.prototype),this.name="AuthMissingError",this.pluginId=n,this.authType=t}};var Co=300;async function Un(e,n){let{keys:t,hub:r,plugin:o,tenantId:i}=e,s=n?.forceRefresh??!1,[a,c,d]=await Promise.all([t.get_access_token(),t.get_expires_at(),t.get_refresh_token()]);if(!a&&!d)throw new ne(o,"managed");let l=Math.floor(Date.now()/1e3);if(!s&&a&&c&&Number(c)>l+Co)return{accessToken:a,expiresAt:Number(c),refreshed:!1};if(!d&&a&&!s)return{accessToken:a,expiresAt:c?Number(c):l+3600,refreshed:!1};let u=await me({hub:r,path:"/oauth/refresh",body:{plugin:o,tenantId:i},parseResponse:pt}),g=u.expires_in?l+u.expires_in:c?Number(c):l+3600;return await t.set_access_token(u.access_token),await t.set_expires_at(String(g)),u.refresh_token&&await t.set_refresh_token(u.refresh_token),u.scope&&await t.set_scope(u.scope),{accessToken:u.access_token,expiresAt:g,refreshed:!0}}async function Ft(e,n){e._refreshAuth=async()=>(await Un(n,{forceRefresh:!0})).accessToken}async function Nn(e,n){return me({hub:e,path:"/permission/sessions",notFoundMessage:"Hub REST API not found at /permission/sessions. Check HUB_API_URL and ensure the Hub API is deployed.",body:{permissionId:n.permissionId,permissionToken:n.permissionToken,plugin:n.plugin,endpoint:n.endpoint,args:n.args,tenantId:n.tenantId,deliveryUrl:e.deliveryUrl,expiresAt:n.expiresAt},parseResponse:lt})}function Wn(e){return`Approval required. Visit ${e} to approve or deny, then tell the agent to retry this action.`}function Bt(e,n){return{delivery:t=>Qe(e,t),createConnectSession:t=>je(e,t,n)}}function Xe(e,n){let t=[];e||t.push("database"),n||t.push("kek");let r={};return new Proxy(r,{get(o,i){let s=t.length>1;throw new Error(`corsair.keys.${String(i)}: Cannot access keys because ${t.join(" and ")} ${s?"are":"is"} not configured. Provide both 'database' and 'kek' in createCorsair() to enable key management.
|
|
5
|
+
`))}}return u}async function qr(e,n,t,r){let o=new Set;for(let i of e.values())await Zr(i.pluginId,i.authType,i.integration,i.account,i.integrationFields,i.accountFields,n,t,r)&&o.add(i.pluginId);return o}async function Gr(e,n,t,r,o){if(!Br(wn)){o("[corsair:setup] Backfill config is invalid - skipping backfill.");return}let i=wn,s=new Set(n.map(a=>a.id));for(let[a,c]of Object.entries(i)){if(!s.has(a))continue;if(!t.has(a)){r(`[corsair:setup] Skipping backfill for '${a}' \u2014 auth not configured.`);continue}let d=ie(e)?e[a]:void 0,l=ie(d)?d.api:void 0;if(l)for(let[u,g]of Object.entries(c))for(let[y,p]of Object.entries(g)){r(`[corsair:setup] Backfilling ${a} \u203A ${u}.${y}...`);try{let f=ie(l)?l[u]:void 0;await z(f,y)?.(p)}catch(f){o(`[corsair:setup] ${a} \u203A ${u}.${y} failed: `+(f instanceof Error?f.message:String(f)))}}}}async function we(e,n){if(!P(e).database)throw new Error("A database must be configured to provision Corsair for connect");await ht(e,{tenantId:n})}var bt=new Set(["webhook_signature","expires_at","scope"]);function zr(e){return e==="oauth_2"||e==="managed"?"oauth":e==="bot_token"?"bot_token":"api_key"}async function Jr(e,n,t,r){let o=Re(n,t).filter(c=>!bt.has(c));if(o.length===0)return!0;let s=(typeof e.withTenant=="function"?e.withTenant(r):e)[n.id],a=s&&typeof s=="object"&&"keys"in s?s.keys:null;if(!a)return!1;for(let c of o){if((t==="oauth_2"||t==="managed")&&c==="refresh_token")continue;let d=z(a,`get_${c}`);if(!d)continue;let l=null;try{let u=await d();l=typeof u=="string"?u:null}catch{l=null}if(!l)return!1}return!0}function Vr(e,n){return e==="oauth_2"||e==="managed"?[]:n.filter(t=>!bt.has(t))}async function wt(e,n,t={}){let r=P(e),o=M(e),i=Ue(o),s=[],a=t.pluginIds?new Set(t.pluginIds):null;for(let c of r.plugins){if(a&&!a.has(c.id))continue;let d=se(c);if(!d)continue;let l=zr(d),u=ve(c.id),g=await Jr(e,c,d,n),y=Re(c,d),p={plugin:c.id,providerName:u,authKind:l,alreadyConfigured:g};if(l==="oauth"){let h=t.oauthModeOverrides?.[c.id]??(d==="managed"?"managed":"byo");if(p.oauthMode=h,!g)if(h==="managed")p.state=ae(J(c.id,n),r.kek);else try{let m=await Tn(e,c.id,{tenantId:n,redirectUri:i});p.oauthUrl=m.url,p.state=m.state}catch{continue}}else{let f=Vr(d,y);f.length>0&&(p.credentialFields=f)}s.push(p)}return s}function Ct(e,n){let t=M(e);if(n){let r=be({source:n,deliveryUrl:t.deliveryUrl});if(r)throw new Error(r.error);return n}return ke(t.deliveryUrl)}async function Tt(e,n){await we(e,n)}async function Le(e,n){let t=M(e);await Tt(e,n.tenantId);let r=n.plugin?[n.plugin]:void 0,o=n.plugin&&n.oauthMode?{[n.plugin]:n.oauthMode}:void 0,i=await wt(e,n.tenantId,{pluginIds:r,oauthModeOverrides:o});if(i.length===0)throw new Error(n.plugin?`Plugin '${n.plugin}' is not configured on this Corsair instance`:"No plugins are configured on this Corsair instance");let s=i.filter(c=>!c.alreadyConfigured);if(s.length===0)throw new Error(n.plugin?`Plugin '${n.plugin}' is already configured for this tenant`:"All plugins are already configured for this tenant");let a=Ct(e,n.source);return me({hub:t,path:"/connect/sessions",notFoundMessage:"Hub REST API not found at /connect/sessions. Check HUB_API_URL and ensure the Hub API is deployed.",body:{tenantId:n.tenantId,deliveryUrl:t.deliveryUrl,source:a,plugins:s},parseResponse:dt})}function Yr(e){if(e==="byo"||e==="managed")return e}function Qr(e){if(e==="client"||e==="server")return e}function Pn(e){let n=e.plugin?.trim(),t=e.tenantId?.trim()||"default",r=Qr(e.source),o=Yr(e.oauthMode),i=e.providerName?.trim();if(e.source!==void 0&&e.source!==null&&String(e.source).trim()!==""&&!r)return{error:'source must be "client" or "server"',status:400};if(e.oauthMode&&!o)return{error:'oauthMode must be "byo" or "managed"',status:400};let s={tenantId:t,oauthMode:o};return n&&(s.plugin=n),r&&(s.source=r),i&&(s.providerName=i),s}function xn(e){let{searchParams:n}=new URL(e),t=n.get("oauthMode");return{plugin:n.get("plugin")??void 0,tenantId:n.get("tenantId")??void 0,source:n.get("source")??void 0,oauthMode:t??void 0,providerName:n.get("providerName")??void 0}}function Xr(e){return{ok:!0,connectUrl:e.connectUrl,token:e.token,projectId:e.projectId,expiresAt:e.expiresAt}}async function eo(e,n,t){if(t?.resolveTenantId){let r=await t.resolveTenantId(e);return r?.trim()?r.trim():{error:"Unauthorized",status:401}}return n.tenantId?.trim()||t?.defaultTenantId?.trim()||"default"}async function An(e,n,t){let o=n.method.toUpperCase()==="GET"?xn(n.url):await n.json().catch(()=>null);if(!o)return{error:"Invalid JSON body",status:400};let i=await eo(n,o,t);if(typeof i!="string")return i;let s=Pn({...o,tenantId:i});if("error"in s)return s;let a=M(e);if(s.source){let l=be({source:s.source,deliveryUrl:a.deliveryUrl,oauthMode:s.oauthMode});if(l)return l}let c={...s,source:s.source??ke(a.deliveryUrl)},d=await Le(e,c);return Xr(d)}async function Rn(e,n,t){try{let r=await An(e,n,t);return"error"in r?Response.json({error:r.error},{status:r.status}):Response.json(r)}catch(r){if(r instanceof V)return Response.json({error:r.message},{status:503});let o=r instanceof Error?r.message:String(r);return Response.json({error:o},{status:500})}}async function je(e,n,t){let r=n.method.toUpperCase();return r!=="GET"&&r!=="POST"?Response.json({error:"Method not allowed"},{status:405}):Rn(e,n,t)}var Y=class extends Error{code;constructor(n,t){super(t),this.name="AuthCredentialsDeliveryError",this.code=n}};async function Pt(e,n){let t=P(e,()=>new Y("invalid_corsair_instance","Invalid corsair instance"));if(!t.database)throw new Y("no_database","Database not configured");let r=B(t,n.plugin,d=>new Y("plugin_not_found",d)),o=se(r);if(!o)throw new Y("invalid_credentials",`Plugin '${r.id}' has no authType configured`);if(o==="oauth_2"||o==="managed")throw new Y("invalid_credentials","OAuth plugins must be connected via sign-in, not credentials delivery");await we(e,n.tenantId);let i=new Set(Re(r,o)),a=r.authConfig?.[o]?.account??[],c=I({authType:o,integrationName:n.plugin,tenantId:n.tenantId,kek:t.kek,database:t.database,extraAccountFields:a});for(let[d,l]of Object.entries(n.credentials)){if(!l.trim())continue;if(!i.has(d))throw new Y("invalid_credentials",`Unknown credential field '${d}' for plugin '${n.plugin}'`);let u=z(c,`set_${d}`);if(!u)throw new Y("invalid_credentials",`Cannot set credential field '${d}' for plugin '${n.plugin}'`);await u(l.trim())}return{plugin:n.plugin,tenantId:n.tenantId}}async function Ke(e,n,t){for(let r of e){if(r.id!==n)continue;let o=r.oauthWebhookTenantLinkResolver;return o?o(t):null}return null}import{createCipheriv as xt,createDecipheriv as At,randomBytes as Ze,scrypt as no}from"crypto";import{promisify as to}from"util";var Rt=to(no),qe="aes-256-gcm",vt=12,Ge=16,ro=16,Ee=32;function Q(){return Ze(Ee).toString("base64")}async function X(e,n){let t=Ze(ro),r=await Rt(n,t,Ee),o=Ze(vt),i=xt(qe,r,o,{authTagLength:Ge}),s=Buffer.concat([i.update(e,"utf8"),i.final()]),a=i.getAuthTag();return[t.toString("base64"),o.toString("base64"),a.toString("base64"),s.toString("base64")].join(":")}async function W(e,n){let[t,r,o,i]=e.split(":");if(!t||!r||!o||!i)throw new Error("Invalid encrypted DEK format");let s=Buffer.from(t,"base64"),a=Buffer.from(r,"base64"),c=Buffer.from(o,"base64"),d=Buffer.from(i,"base64"),l=await Rt(n,s,Ee),u=At(qe,l,a,{authTagLength:Ge});return u.setAuthTag(c),Buffer.concat([u.update(d),u.final()]).toString("utf8")}function vn(e,n){let t=Buffer.from(n,"base64"),r=Ze(vt),o=xt(qe,t,r,{authTagLength:Ge}),i=Buffer.concat([o.update(e,"utf8"),o.final()]),s=o.getAuthTag();return[r.toString("base64"),s.toString("base64"),i.toString("base64")].join(":")}function Oe(e,n){let[t,r,o]=e.split(":");if(!t||!r||!o)throw new Error("Invalid encrypted data format");let i=Buffer.from(n,"base64"),s=Buffer.from(t,"base64"),a=Buffer.from(r,"base64"),c=Buffer.from(o,"base64"),d=At(qe,i,s,{authTagLength:Ge});return d.setAuthTag(a),Buffer.concat([d.update(c),d.final()]).toString("utf8")}function ce(e,n){let t={};for(let[r,o]of Object.entries(e))t[r]=vn(o,n);return t}function ee(e,n){let t={};for(let[r,o]of Object.entries(e))t[r]=Oe(o,n);return t}function De(e,n,t){let r=ee(e,n);return ce(r,t)}function It(e,n,t){let r={};for(let o of t)r[`get_${o}`]=async()=>(await e())[o]??null,r[`set_${o}`]=async i=>{let s=[null,void 0,""].includes(i)?null:i;await n({[o]:s})};return r}var In=e=>{if(!e)return{};if(typeof e=="string")try{return JSON.parse(e)}catch{return{}}return e};function D(e){let{authType:n,integrationName:t,kek:r,database:o,extraIntegrationFields:i=[]}=e,s=[...U[n].integration,...i],a=null,c={kek:r,integrationName:t,getIntegration:async()=>{if(a)return a;let p=await o.db.selectFrom("corsair_integrations").selectAll().where("name","=",t).executeTakeFirst();if(!p)throw new Error(`Integration "${t}" not found. Make sure to create the integration first.`);return a={id:p.id,config:In(p.config),dek:p.dek??null},a},updateIntegration:async p=>{let f=await c.getIntegration();await o.db.updateTable("corsair_integrations").set({...p.config!==void 0?{config:p.config}:{},...p.dek!==void 0?{dek:p.dek}:{},updated_at:new Date}).where("id","=",f.id).execute(),a=null}},d=null,l=async()=>{if(d)return d;let p=await c.getIntegration();if(!p.dek)throw new Error(`No DEK found for integration "${t}". Initialize the integration first.`);return d=await W(p.dek,r),d},u=async()=>{let p=await c.getIntegration(),f=await l(),h=p.config;return!h||Object.keys(h).length===0?{}:ee(h,f)};return{get_dek:l,issue_new_dek:async()=>{let p=await c.getIntegration(),f=Q(),h={};if(p.dek){let C=await W(p.dek,r),b=p.config;b&&Object.keys(b).length>0&&(h=De(b,C,f))}let m=await X(f,r);return await c.updateIntegration({config:h,dek:m}),d=f,f},...It(u,async p=>{let f=await l(),h;try{h=await u()}catch(b){console.error(`[corsair] Failed to decrypt config for integration "${t}", starting fresh:`,b),h={}}let m={...h};for(let[b,k]of Object.entries(p))k===null?delete m[b]:m[b]=k;let C=ce(m,f);await c.updateIntegration({config:C})},s)}}function I(e){let{authType:n,integrationName:t,tenantId:r,kek:o,database:i,extraAccountFields:s=[]}=e,a=[...U[n].account,...s],c=null,d=null,l=async()=>{if(d)return d;let k=await i.db.selectFrom("corsair_integrations").selectAll().where("name","=",t).executeTakeFirst();if(!k)throw new Error(`Integration "${t}" not found. Make sure to create the integration first.`);return d={id:k.id,config:In(k.config),dek:k.dek??null},d},u={kek:o,integrationName:t,tenantId:r,getIntegration:l,getAccount:async()=>{if(c)return c;let k=await l(),w=await i.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",r).where("integration_id","=",k.id).executeTakeFirst();if(!w)throw new Error(`Account not found for tenant "${r}" and integration "${t}". Make sure to create the account first.`);return c={id:w.id,config:In(w.config),dek:w.dek??null},c},updateAccount:async k=>{let w=await u.getAccount();await i.db.updateTable("corsair_accounts").set({...k.config!==void 0?{config:k.config}:{},...k.dek!==void 0?{dek:k.dek}:{},updated_at:new Date}).where("id","=",w.id).execute(),c=null}},g=null,y=null,p=async()=>{if(g)return g;let k=await u.getAccount();if(!k.dek)throw new Error(`No DEK found for account (tenant: "${r}", integration: "${t}"). Initialize the account first.`);return g=await W(k.dek,o),g},f=async()=>{if(y)return y;let k=await u.getIntegration();if(!k.dek)throw new Error(`No DEK found for integration "${t}". Initialize the integration first.`);return y=await W(k.dek,o),y},h=async()=>{let k=await u.getAccount(),w=await p(),T=k.config;return!T||Object.keys(T).length===0?{}:ee(T,w)},m=async()=>{let k=await u.getIntegration(),w=await f(),T=k.config;return!T||Object.keys(T).length===0?{}:ee(T,w)},b={get_dek:p,issue_new_dek:async()=>{let k=await u.getAccount(),w=Q(),T={};if(k.dek){let _=await W(k.dek,o),E=k.config;E&&Object.keys(E).length>0&&(T=De(E,_,w))}let S=await X(w,o);return await u.updateAccount({config:T,dek:S}),g=w,w},...It(h,async k=>{let w=await p(),T;try{T=await h()}catch(E){console.error(`[corsair] Failed to decrypt config for account (tenant: "${r}", integration: "${t}"), starting fresh:`,E),T={}}let S={...T};for(let[E,x]of Object.entries(k))x===null?delete S[E]:S[E]=x;let _=ce(S,w);await u.updateAccount({config:_})},a)};return n==="oauth_2"&&(b.get_integration_credentials=async()=>{let k=await m();return{client_id:k.client_id||null,client_secret:k.client_secret||null,redirect_url:k.redirect_url??null}}),b}async function St(e,n,t){let r=await e.db.selectFrom("corsair_integrations").selectAll().where("name","=",n).executeTakeFirst();if(!r)throw new Error(`Integration "${n}" not found.`);let o=Q(),i=await X(o,t);return await e.db.updateTable("corsair_integrations").set({dek:i,updated_at:new Date}).where("id","=",r.id).execute(),o}async function _t(e,n,t,r){let o=await e.db.selectFrom("corsair_integrations").selectAll().where("name","=",n).executeTakeFirst();if(!o)throw new Error(`Integration "${n}" not found.`);let i=await e.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",t).where("integration_id","=",o.id).executeTakeFirst();if(!i)throw new Error(`Account not found for tenant "${t}" and integration "${n}".`);let s=Q(),a=await X(s,r);return await e.db.updateTable("corsair_accounts").set({dek:a,updated_at:new Date}).where("id","=",i.id).execute(),s}var Et=e=>{if(!e)return{};if(typeof e=="string")try{return JSON.parse(e)}catch{return{}}return e};async function oo(e){let n=await e.database.db.selectFrom("corsair_integrations").selectAll().where("name","=",e.pluginId).executeTakeFirst();if(!n)throw new Error(`Integration '${e.pluginId}' not found. Run setupCorsair before registering webhook tenant links.`);let t=await e.database.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",e.tenantId).where("integration_id","=",n.id).executeTakeFirst();if(!t)throw new Error(`Account not found for tenant '${e.tenantId}' and integration '${e.pluginId}'.`);return{integrationId:n.id,accountId:t.id}}async function io(e){let n=await e.database.db.selectFrom("corsair_accounts").selectAll().where("id","=",e.accountId).executeTakeFirst();if(!n?.dek)throw new Error(`Account '${e.accountId}' has no DEK.`);let t=await W(n.dek,e.kek),r=Et(n.config),o={};Object.keys(r).length>0&&(o=ee(r,t)),o[e.link.linkType]=e.link.externalId;let i=ce(o,t);await e.database.db.updateTable("corsair_accounts").set({config:i,updated_at:new Date}).where("id","=",n.id).execute()}async function Ce(e){let{database:n,kek:t,pluginId:r,tenantId:o,link:i,authType:s,extraAccountFields:a=[]}=e,{accountId:c}=await oo({database:n,pluginId:r,tenantId:o}),d=!1;if(s){let l=I({authType:s,integrationName:r,tenantId:o,kek:t,database:n,extraAccountFields:a}),u=`set_${i.linkType}`,g=l[u];typeof g=="function"&&(await g(i.externalId),d=!0)}d||await io({database:n,kek:t,accountId:c,link:i})}async function Sn(e){let{database:n,kek:t,pluginId:r,linkType:o,externalId:i}=e,s=await n.db.selectFrom("corsair_accounts as accounts").innerJoin("corsair_integrations as integrations","integrations.id","accounts.integration_id").selectAll("accounts").where("integrations.name","=",r).execute();for(let a of s)if(a.dek)try{let c=await W(a.dek,t),l=Et(a.config)[o];if(!l)continue;if(Oe(l,c)===i)return a}catch{continue}return null}async function ze(e){return(await Sn(e))?.tenant_id??null}async function Ot(e){return ze({database:e.database,kek:e.kek,pluginId:e.pluginId,linkType:e.match.linkType,externalId:e.match.externalId})}var ue=class extends Error{code;constructor(n,t){super(t),this.name="ManagedOAuthDeliveryError",this.code=n}};async function Te(e,n){let{plugin:t,tenantId:r,accessToken:o,refreshToken:i,expiresIn:s,scope:a}=n;if(!o.trim())throw new ue("no_access_token","Managed OAuth delivery missing access_token");let c=P(e,()=>new ue("invalid_corsair_instance","Invalid corsair instance"));if(!c.database)throw new ue("no_database","No database configured on corsair instance");let d=B(c,t,u=>new ue("plugin_not_found",u));await we(e,r);let l=I({authType:"managed",integrationName:t,tenantId:r,kek:c.kek,database:c.database});await l.set_access_token(o),i&&await l.set_refresh_token(i),s&&await l.set_expires_at(String(Math.floor(Date.now()/1e3)+s)),a&&await l.set_scope(a);try{let u=await Ke(c.plugins,t,{access_token:o,refresh_token:i,scope:a});if(u)try{let g=d.authConfig?.managed?.account??[];await Ce({database:c.database,kek:c.kek,pluginId:t,tenantId:r,link:u,authType:"managed",extraAccountFields:g})}catch(g){console.warn(`[corsair:managed-oauth] Failed to persist webhook tenant link for '${t}' tenant '${r}':`,g)}}catch(u){console.warn(`[corsair:managed-oauth] Failed to resolve webhook tenant link for '${t}' tenant '${r}':`,u)}return{plugin:t,tenantId:r}}function so(e){return e!==null&&typeof e=="object"&&"match"in e&&"handler"in e&&typeof e.match=="function"&&typeof e.handler=="function"}function Dt(e,n,t=[]){for(let[r,o]of Object.entries(e))if(so(o)){if(o.match(n))return{webhook:o,path:[...t,r]}}else if(o&&typeof o=="object"){let i=Dt(o,n,[...t,r]);if(i)return i}return null}function ao(e){let n={};for(let[t,r]of Object.entries(e))n[t.toLowerCase()]=Array.isArray(r)?r[0]:r;return n}function co(e){let n=e["x-goog-resource-uri"],t=e["x-goog-channel-id"];if(!n||!t)return null;let r={resourceId:e["x-goog-resource-id"]||"",resourceState:e["x-goog-resource-state"]||"",resourceUri:n,channelId:t,channelExpiration:e["x-goog-channel-expiration"]||""};return n.includes("/drive/")&&(r.kind="drive#change"),{message:{data:Buffer.from(JSON.stringify(r)).toString("base64"),messageId:e["x-goog-message-number"]||""}}}async function Ht(e,n,t,r){let o=ao(n),i=typeof t=="string"?JSON.parse(t):t;(!i||typeof i=="object"&&Object.keys(i).length===0)&&o["x-goog-resource-uri"]&&(i=co(o)||i);let a={headers:o,body:i,...r?{query:r}:{}},c=r?.tenantId||"default",d=e.withTenant?e.withTenant(c):e,l=Fe;for(let u of l){let g=d[u];if(!g||!g.webhooks||g.pluginWebhookMatcher&&!g.pluginWebhookMatcher(a))continue;let y=Dt(g.webhooks,a);if(!y)continue;let p=y.path.join("."),f={payload:i,headers:o,rawBody:typeof t=="string"?t:JSON.stringify(t),...r?{query:r}:{}};try{let h=await y.webhook.handler(f),m=!!Object.keys(h.returnToSender||{})?.length;return{plugin:u,action:p,body:i,response:m?{...h?.returnToSender,success:!0}:{success:!0},...h.responseHeaders&&{responseHeaders:h.responseHeaders}}}catch(h){return console.error(`Error executing webhook handler for ${u}.${p}:`,h),{plugin:u,action:p,body:i,response:{success:!1,error:h instanceof Error?h.message:"Unknown error"}}}}return{plugin:null,action:null,body:null}}import{createHmac as uo,timingSafeEqual as lo}from"crypto";var po=300*1e3;function go(e){if(e)return e.startsWith("sha256=")?e.slice(7):e}function _n(e){let n=e.signingSecret.trim();if(!n)return{ok:!1,error:"Tunnel signing secret is required"};let t=go(e.signatureHeader);if(!t)return{ok:!1,error:"Invalid tunnel signature"};let r=Number(e.timestampHeader);if(!Number.isFinite(r))return{ok:!1,error:"Invalid or missing tunnel timestamp"};if(Math.abs(Date.now()-r*1e3)>po)return{ok:!1,error:"Tunnel request timestamp is outside the allowed window"};let i=uo("sha256",n).update(e.body).digest("hex");try{if(!lo(Buffer.from(i,"utf8"),Buffer.from(t,"utf8")))return{ok:!1,error:"Invalid tunnel signature"}}catch{return{ok:!1,error:"Invalid tunnel signature"}}return{ok:!0}}import{createHmac as fo,timingSafeEqual as yo}from"crypto";function Je(e){return e.deliveryMode==="permission.approve"||e.deliveryMode==="permission.deny"}function Ve(e){return e.deliveryMode==="oauth.tokens"||typeof e.accessToken=="string"&&e.accessToken.length>0}function Mt(e){let n=e.trim();return n.length>0?n:null}function ho(e,n){let t=Mt(n);if(!t)throw new Error("Signing secret is required for browser delivery tokens");return fo("sha256",t).update(e).digest("base64url")}function Ye(e,n){let t=Mt(n);if(!t)return null;let r=e.split(".");if(r.length!==2)return null;let[o,i]=r;if(!o||!i)return null;let s=ho(o,t);try{if(!yo(Buffer.from(i,"utf8"),Buffer.from(s,"utf8")))return null}catch{return null}let a;try{a=JSON.parse(Buffer.from(o,"base64url").toString("utf8"))}catch{return null}return a.exp*1e3<Date.now()?null:a}function $t(e,n){if(e instanceof Headers)return e.get(n)??void 0;let t=e[n]??e[n.toLowerCase()];return Array.isArray(t)?t[0]:typeof t=="string"?t:void 0}async function mo(e,n,t){let r=typeof t.tenantId=="string"?t.tenantId:typeof t.query?.tenantId=="string"?t.query.tenantId:void 0;return r||(!n.database||!t.plugin||!t.linkType||!t.externalId?void 0:await ze({database:n.database,kek:n.kek,pluginId:t.plugin,linkType:t.linkType,externalId:t.externalId})??void 0)}async function ko(e,n,t){let r=await mo(e,n,t),o={...t.query??{},...r?{tenantId:r}:{}},i=await Ht(e,t.headers,t.body,o);if(!i.plugin)return{status:"failed",retryable:!1,error:"No matching webhook handler found"};if(i.response&&i.response.success===!1)return{status:"failed",retryable:!1,error:typeof i.response.error=="string"?i.response.error:"Webhook handler failed"};let s=i.response?.returnToSender,a=s&&typeof s=="object"&&typeof s.validationToken=="string"&&Object.keys(s).length===1?s.validationToken:s||(i.response?.data??i.response);return{status:"ok",webhookResponse:{status:i.response?.statusCode??200,body:a,headers:i.responseHeaders}}}async function bo(e,n){return await He(e,n),{status:"ok"}}async function wo(e,n){return await Te(e,{plugin:n.plugin,tenantId:n.tenantId,accessToken:n.accessToken,refreshToken:n.refreshToken,expiresIn:n.expiresIn,scope:n.scope}),{status:"ok"}}async function On(e,n,t){let r=await En(e,{token:n},t);if(r.status!=="ok")throw new Error(r.error??"Permission decision failed")}async function En(e,n,t){let r=P(e),o=n.token?.trim();if(!o)return{status:"failed",retryable:!1,error:"Permission token is required"};if(!r.database)return{status:"failed",retryable:!1,error:"Database not configured"};let i=new Date().toISOString(),s=await r.database.db.selectFrom("corsair_permissions").selectAll().where("token","=",o).executeTakeFirst();return s?s.status!=="pending"?{status:"ok"}:s.expires_at<i?(await r.database.db.updateTable("corsair_permissions").set({status:"expired",updated_at:new Date}).where("id","=",s.id).execute(),{status:"failed",retryable:!1,error:"Permission has expired"}):(await r.database.db.updateTable("corsair_permissions").set({status:t,updated_at:new Date}).where("id","=",s.id).execute(),{status:"ok"}):{status:"failed",retryable:!1,error:"Permission not found"}}async function Co(e,n){try{return await Pt(e,n),{status:"ok"}}catch(t){return{status:"failed",retryable:!1,error:t instanceof Error?t.message:"Credential delivery failed"}}}async function Dn(e,n,t={}){let r=P(e),o=$t(n.headers,"x-corsair-signature"),i=$t(n.headers,"x-corsair-timestamp");if(t.signingSecret?.trim()){let a=_n({body:n.body,signatureHeader:o,timestampHeader:i,signingSecret:t.signingSecret});if(!a.ok)return{status:"failed",retryable:!1,error:a.error}}else if(!t.allowUnsignedTunnel)return{status:"failed",retryable:!1,error:"Tunnel signing secret is required"};let s;try{s=JSON.parse(n.body)}catch{return{status:"failed",retryable:!1,error:"Invalid tunnel envelope JSON"}}switch(s.type){case"webhook":return ko(e,r,s.payload);case"oauth.callback":return bo(e,s.payload);case"oauth.tokens":return wo(e,s.payload);case"permission.approve":return En(e,s.payload,"approved");case"permission.deny":return En(e,s.payload,"denied");case"auth.credentials":return Co(e,s.payload);default:return{status:"failed",retryable:!1,error:`Unsupported tunnel type: ${s.type}`}}}async function Hn(e,n){let t=M(e),o=new URL(n).searchParams.get("d");if(!o)return{type:"json",status:200,body:{status:"ok",message:"Corsair tunnel endpoint is active",timestamp:new Date().toISOString()}};let i=Ye(o,t.signingSecret);if(!i)return{type:"json",status:400,body:{error:"Invalid or expired delivery token"}};try{if(Je(i)){if(!i.permissionToken)return{type:"json",status:400,body:{error:"Permission delivery missing permission token"}};await On(e,i.permissionToken,i.deliveryMode==="permission.approve"?"approved":"denied")}else if(Ve(i)){if(!i.accessToken)return{type:"json",status:400,body:{error:"Managed OAuth delivery missing access_token"}};await Te(e,{plugin:i.plugin,tenantId:i.tenantId,accessToken:i.accessToken,refreshToken:i.refreshToken,expiresIn:i.expiresIn,scope:i.scope})}else{if(!i.code||!i.state||!i.redirectUri)return{type:"json",status:400,body:{error:"Invalid BYO OAuth delivery token"}};await He(e,{code:i.code,state:i.state,redirectUri:i.redirectUri})}}catch(s){return{type:"json",status:400,body:{error:s instanceof Error?s.message:"Hub delivery failed"}}}return{type:"redirect",url:i.hubSuccessUrl}}async function Mn(e,n){let t=M(e),r=await Dn(e,n,{signingSecret:t.signingSecret});if(r.status!=="ok")return{type:"json",status:r.retryable===!1?400:502,body:{error:r.error??"Tunnel processing failed"}};let o=r.webhookResponse;if(!o)return{type:"json",status:200,body:{status:"ok"}};let i=o.status??200,s=o.headers;return o.body&&typeof o.body=="object"&&!(o.body instanceof ArrayBuffer)?{type:"json",status:i,body:o.body,headers:s}:{type:"text",status:i,body:typeof o.body=="string"?o.body:o.body?JSON.stringify(o.body):null,headers:s}}function $n(e){if(e.type==="redirect")return Response.redirect(e.url,302);let n=new Headers;for(let[t,r]of Object.entries(e.headers??{}))typeof r=="string"&&n.set(t,r);return e.type==="json"?Response.json(e.body,{status:e.status,headers:n}):new Response(e.body,{status:e.status,headers:n})}async function Fn(e,n){return n.method==="GET"?Hn(e,n.url):Mn(e,{headers:n.headers,body:n.body??""})}async function Bn(e,n){try{let t=await Fn(e,n);return $n(t)}catch(t){if(t instanceof V)return Response.json({error:t.message},{status:503});throw t}}async function Qe(e,n){let t=n.method.toUpperCase();return t!=="GET"&&t!=="POST"?Response.json({error:"Method not allowed"},{status:405}):Bn(e,{method:t,url:n.url,headers:n.headers,body:t==="POST"?await n.text():void 0})}var ne=class extends Error{pluginId;authType;constructor(n,t,r){super(r??`[auth-missing:${n}:${t}]`),Object.setPrototypeOf(this,new.target.prototype),this.name="AuthMissingError",this.pluginId=n,this.authType=t}};var To=300;async function Un(e,n){let{keys:t,hub:r,plugin:o,tenantId:i}=e,s=n?.forceRefresh??!1,[a,c,d]=await Promise.all([t.get_access_token(),t.get_expires_at(),t.get_refresh_token()]);if(!a&&!d)throw new ne(o,"managed");let l=Math.floor(Date.now()/1e3);if(!s&&a&&c&&Number(c)>l+To)return{accessToken:a,expiresAt:Number(c),refreshed:!1};if(!d&&a&&!s)return{accessToken:a,expiresAt:c?Number(c):l+3600,refreshed:!1};let u=await me({hub:r,path:"/oauth/refresh",body:{plugin:o,tenantId:i},parseResponse:pt}),g=u.expires_in?l+u.expires_in:c?Number(c):l+3600;return await t.set_access_token(u.access_token),await t.set_expires_at(String(g)),u.refresh_token&&await t.set_refresh_token(u.refresh_token),u.scope&&await t.set_scope(u.scope),{accessToken:u.access_token,expiresAt:g,refreshed:!0}}async function Ft(e,n){e._refreshAuth=async()=>(await Un(n,{forceRefresh:!0})).accessToken}async function Nn(e,n){return me({hub:e,path:"/permission/sessions",notFoundMessage:"Hub REST API not found at /permission/sessions. Check HUB_API_URL and ensure the Hub API is deployed.",body:{permissionId:n.permissionId,permissionToken:n.permissionToken,plugin:n.plugin,endpoint:n.endpoint,args:n.args,tenantId:n.tenantId,deliveryUrl:e.deliveryUrl,expiresAt:n.expiresAt},parseResponse:lt})}function Wn(e){return`Approval required. Visit ${e} to approve or deny, then tell the agent to retry this action.`}function Bt(e,n){return{delivery:t=>Qe(e,t),createConnectSession:t=>je(e,t,n)}}function Xe(e,n){let t=[];e||t.push("database"),n||t.push("kek");let r={};return new Proxy(r,{get(o,i){let s=t.length>1;throw new Error(`corsair.keys.${String(i)}: Cannot access keys because ${t.join(" and ")} ${s?"are":"is"} not configured. Provide both 'database' and 'kek' in createCorsair() to enable key management.
|
|
6
6
|
|
|
7
|
-
To generate a KEK, run: openssl rand -base64 ${Ee}`)}})}var
|
|
7
|
+
To generate a KEK, run: openssl rand -base64 ${Ee}`)}})}var Po=async(e,n)=>(console.error(`[corsair:${n.pluginId}:${n.operation}]`,{error:e.message,input:n.input}),{maxRetries:0});async function Ut(e,n,t,r,o){let i={pluginId:n,operation:t,input:r,originalError:e},s=Object.keys(o).find(d=>o[d]?.match(e,i));return await(o[s||"DEFAULT"]?.handler||Po)(e,i)}import{randomBytes as xo}from"crypto";import{v4 as Ao}from"uuid";var Ro={open:{read:"allow",write:"allow",destructive:"allow"},cautious:{read:"allow",write:"allow",destructive:"require_approval"},strict:{read:"allow",write:"require_approval",destructive:"deny"},readonly:{read:"allow",write:"deny",destructive:"deny"}};function vo(e,n,t){return t!==void 0?t:Ro[n][e]}function Ln(e){let n=/(\d+)(d|h|m|s)/g,t=0,r;for(;(r=n.exec(e))!==null;){let o=parseInt(r[1],10);switch(r[2]){case"d":t+=o*864e5;break;case"h":t+=o*36e5;break;case"m":t+=o*6e4;break;case"s":t+=o*1e3;break}}return t>0?t:600*1e3}function Wt(e){return{async find_by_permission_id(n){if(e)return e.db.selectFrom("corsair_permissions").selectAll().where("id","=",n).executeTakeFirst()},async find_by_token(n){if(e)return e.db.selectFrom("corsair_permissions").selectAll().where("token","=",n).executeTakeFirst()},async set_executing(n){e&&await e.db.updateTable("corsair_permissions").set({status:"executing",updated_at:new Date}).where("id","=",n).execute()},async set_completed(n){e&&await e.db.updateTable("corsair_permissions").set({status:"completed",updated_at:new Date}).where("id","=",n).execute()}}}async function Nt(e,n,t){let r=Date.now()+t;for(;Date.now()<r;){let o=await e.db.selectFrom("corsair_permissions").select(["id","status"]).where("id","=",n).executeTakeFirst();if(!o)return{result:"blocked",reason:"pending"};if(o.status==="approved")return{result:"allow",onComplete:async()=>{await e.db.updateTable("corsair_permissions").set({status:"completed",updated_at:new Date}).where("id","=",n).execute()}};if(o.status==="denied")return{result:"blocked",reason:"denied"};if(o.status==="expired"||o.status==="failed")return{result:"blocked",reason:"timeout"};await new Promise(i=>setTimeout(i,500))}return{result:"blocked",reason:"timeout"}}async function Lt(e){let n=vo(e.riskLevel,e.mode,e.override);if(n==="allow")return{result:"allow"};let t=e.meta?.irreversible?" (irreversible)":"",r=e.meta?.description?`${e.meta.description}${t}`:`${e.pluginId}.${e.endpointPath}${t}`;if(n==="deny"||!e.db)return console.log(`[corsair/${e.pluginId}] '${e.endpointPath}' blocked \u2014 denied by permission mode '${e.mode}'.`,`
|
|
8
8
|
Action: ${r}`,`
|
|
9
9
|
To allow this, update the permission mode or add an override in your corsair config.`),{result:"blocked",reason:"policy"};let o=JSON.stringify(e.args),i=new Date().toISOString(),s=e.tenantId??"default",a=await e.db.db.selectFrom("corsair_permissions").selectAll().where("plugin","=",e.pluginId).where("endpoint","=",e.endpointPath).where("args","=",o).where("tenant_id","=",s).where("expires_at",">",i).where("status","in",["pending","approved","executing"]).orderBy("created_at","desc").limit(1).executeTakeFirst();if(a){if(a.status==="approved"){let p=e.db,f=a.id;return{result:"allow",onComplete:async()=>{await p.db.updateTable("corsair_permissions").set({status:"completed",updated_at:new Date}).where("id","=",f).execute()}}}return a.status==="executing"?{result:"allow"}:(console.log(`[corsair/${e.pluginId}] '${e.endpointPath}' blocked \u2014 approval already pending.`,`
|
|
10
10
|
Action: ${r}`,`
|
|
11
11
|
Permission ID: ${a.id}`,`
|
|
12
|
-
Use the token to approve or deny this request.`),(typeof e.approvalMode=="function"?e.approvalMode():e.approvalMode)==="synchronous"?Nt(e.db,a.id,e.timeoutMs??600*1e3):{result:"blocked",reason:"pending",id:a.id,token:a.token,expiresAt:a.expires_at})}let c=
|
|
12
|
+
Use the token to approve or deny this request.`),(typeof e.approvalMode=="function"?e.approvalMode():e.approvalMode)==="synchronous"?Nt(e.db,a.id,e.timeoutMs??600*1e3):{result:"blocked",reason:"pending",id:a.id,token:a.token,expiresAt:a.expires_at})}let c=Ao(),d=xo(32).toString("hex"),l=e.timeoutMs??600*1e3,u=new Date(Date.now()+l).toISOString();return await e.db.db.insertInto("corsair_permissions").values({id:c,created_at:new Date,updated_at:new Date,token:d,plugin:e.pluginId,endpoint:e.endpointPath,args:o,tenant_id:s,status:"pending",expires_at:u}).execute(),console.log(`[corsair/${e.pluginId}] '${e.endpointPath}' blocked \u2014 approval required.`,`
|
|
13
13
|
Action: ${r}`,`
|
|
14
14
|
Permission ID: ${c}`,`
|
|
15
15
|
Permission token: ${d}`,`
|
|
16
16
|
Expires at: ${u}`,`
|
|
17
|
-
Use the token to approve or deny this request.`),(typeof e.approvalMode=="function"?e.approvalMode():e.approvalMode)==="synchronous"?Nt(e.db,c,l):{result:"blocked",reason:"pending",id:c,token:d,expiresAt:u}}function vo(e){return typeof e=="function"}function Io(e,n,t){let r=ae(J(e,n.tenantId??t??"default"),n.kek),o=new URL(n.baseUrl);o.searchParams.set("state",r);let i=o.toString(),s=n.onAuthMissing?n.onAuthMissing({plugin:e,connectUrl:i,state:r}):`[auth-missing:${e}] Authentication required. Direct the user to connect their account: ${i}`;return new Error(s)}function jn({endpoints:e,hooks:n,ctx:t,tree:r,pluginId:o,errorHandlers:i,currentPath:s=[],keyBuilder:a,permissionsConfig:c,endpointMeta:d,database:l,approvalConfig:u,tenantId:g,connectConfig:y,hubConfig:p}){for(let[f,h]of Object.entries(e)){let m=n?.[f];if(vo(h)){let C=m,b=[...s,f].join("."),k=async(w={})=>{let T;if(c){let v=d?.[b],{result:he,reason:q,onComplete:Ae,token:G,id:oe,expiresAt:F}=await Lt({pluginId:o,endpointPath:b,args:w,mode:c.mode,override:c.overrides?.[b],riskLevel:v?.riskLevel??"write",meta:v,db:l,timeoutMs:u?Ln(u.timeout):void 0,tenantId:g,approvalMode:u?.mode});if(he==="blocked"){let O;if(q==="denied")O=`Action '${b}' was denied by the user. Await further instructions before proceeding.`;else if(q==="policy")O=`Action '${b}' is blocked by the permission policy. Update the corsair config to allow it.`;else if(q==="timeout")O=`Action '${b}' timed out waiting for approval.`;else if(G&&oe)if(u?.formatAsyncMessage)O=u.formatAsyncMessage({token:G,id:oe,plugin:o,endpoint:b,args:w});else if(p)try{let pe=u?Ln(u.timeout):6e5,mn=await Nn(p,{permissionId:oe,permissionToken:G,plugin:o,endpoint:b,args:w,tenantId:g??"default",expiresAt:F??new Date(Date.now()+pe).toISOString()});O=Wn(mn.approvalUrl)}catch(pe){let mn=pe instanceof Error?pe.message:String(pe);O=`Action '${b}' requires user approval before it can run. Could not create approval link: ${mn}`}else O=`Action '${b}' requires user approval before it can run.`;else O=`Action '${b}' requires user approval before it can run.`;throw new Error(O)}T=Ae}let S=async(v,he,q)=>{try{return await h(he,q)}catch(Ae){if(Ae instanceof Error){let G=await Ut(Ae,o,b,typeof q=="object"&&q!==null?q:{args:q},i);if(v<(G.maxRetries||0)){let oe=v+1;console.log(`Retrying (${oe} / ${G.maxRetries})...`);let F;if(G.headersRetryAfterMs)F=G.headersRetryAfterMs;else switch(G.retryStrategy){case"exponential_backoff":F=Math.pow(2,oe-1)*1e3;break;case"exponential_backoff_jitter":let O=Math.pow(2,oe-1)*1e3,pe=(Math.random()-.5)*1e3;F=Math.max(0,O+pe);break;case"linear_1s":F=1e3;break;case"linear_2s":F=2e3;break;case"linear_3s":F=3e3;break;case"linear_4s":F=4e3;break;default:F=1e3;break}await new Promise(O=>setTimeout(O,F)),await S(oe,he,q),console.log(`[corsair:${o}:${b}] Retry strategy:`,G)}}throw Ae}},_;try{_=a?await a(t,"endpoint"):void 0}catch(v){throw y?.oauthConfig&&y.kek&&v instanceof ne&&v.authType==="oauth_2"?Io(o,y,g):v}if(!C?.before&&!C?.after){let v=await S(0,{...t,key:_},w);return await T?.(),v}let E={...t,key:_},x=C.before?await C.before(E,w):{ctx:E,args:w,continue:!0,passToAfter:void 0};if(x.continue===!1)return;let ye=await S(0,x.ctx,x.args);return await C.after?.(x.ctx,ye,x.passToAfter),await T?.(),ye};r[f]=k}else if(h&&typeof h=="object"){let C={};jn({endpoints:h,hooks:m,ctx:t,tree:C,pluginId:o,errorHandlers:i,currentPath:[...s,f],keyBuilder:a,permissionsConfig:c,endpointMeta:d,database:l,approvalConfig:u,tenantId:g,connectConfig:y,hubConfig:p}),r[f]=C}}}function So(e){return e!==null&&typeof e=="object"&&"match"in e&&"handler"in e&&typeof e.match=="function"&&typeof e.handler=="function"}function Kn({webhooks:e,hooks:n,ctx:t,webhooksTree:r,keyBuilder:o}){for(let[i,s]of Object.entries(e)){let a=n?.[i];if(So(s)){let c=a,d=async l=>{let u=(y,p)=>s.handler(y,p),g=o?await o(t,"webhook"):void 0;return!c?.before&&!c?.after?u({...t,key:g},l):(async()=>{let y={...t,key:g},p=c.before?await c.before(y,l):{ctx:y,args:l,continue:!0,passToAfter:void 0};if(p.continue===!1)return;let f=await u(p.ctx,p.args);return f?.success===!0&&await c.after?.(p.ctx,f,p.passToAfter),f})()};r[i]={match:s.match,handler:d}}else if(s&&typeof s=="object"){let c={};Kn({webhooks:s,hooks:a,ctx:t,webhooksTree:c,keyBuilder:o}),r[i]=c}}}function _o(e,n,t){let r=null;return async()=>{if(r)return r;if(!e)throw new Error("Database not configured");let o=await e.db.selectFrom("corsair_integrations").selectAll().where("name","=",n).executeTakeFirst();if(!o)throw new Error(`Integration "${n}" not found. Make sure to create the integration first.`);let i=await e.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",t).where("integration_id","=",o.id).executeTakeFirst();if(!i)throw new Error(`Account not found for tenant "${t}" and integration "${n}". Make sure to create the account first.`);return r=i.id,r}}function Eo(e,n,t,r,o){return e?kn(e.db,n,t,r,o):{findByEntityId:async()=>null,findById:async()=>null,findManyByEntityIds:async()=>[],list:async()=>[],search:async()=>[],upsertByEntityId:async()=>{throw new Error("Database not configured")},deleteById:async()=>!1,deleteByEntityId:async()=>!1,count:async()=>0}}function Zn(e,n){let{database:t,tenantId:r,kek:o,rootErrorHandlers:i,approvalConfig:s,connectConfig:a,hubConfig:c}=n,d={},l={};for(let u of e)d[u.id]={},l[u.id]={};for(let u of e){let g=u.schema,y=r??"default",p=_o(t,u.id,y);if(g?.entities){let x={};for(let[ye,v]of Object.entries(g.entities)){let he=t?kn(t.db,p,ye,g.version,v):Eo(void 0,p,ye,g.version,v);x[ye]=he}l[u.id].db=x,d[u.id].db=x}let f=u.options,h=u.authConfig,m;if(t&&o&&f?.authType){let x=h?.[f.authType]?.account??[];m=I({authType:f.authType,integrationName:u.id,tenantId:y,kek:o,database:t,extraAccountFields:x}),d[u.id].keys=m}let C={database:t,db:l[u.id]?.db??{},$getAccountId:p,...u.options?{options:u.options}:{},...m?{keys:m,authType:f?.authType}:{},tenantId:y,...c?{hub:c}:{}},b=u.endpoints??{},k=u.hooks,w={...i,...u.errorHandlers},T={},S=u.options?.permissions;jn({endpoints:b,hooks:k,ctx:C,tree:T,pluginId:u.id,errorHandlers:w,currentPath:[],keyBuilder:u.keyBuilder,permissionsConfig:S,endpointMeta:u.endpointMeta,database:t,approvalConfig:s,tenantId:r,connectConfig:a?{...a,oauthConfig:u.oauthConfig,kek:o,tenantId:y}:void 0,hubConfig:c}),Object.keys(T).length>0&&(d[u.id].api=T),C.endpoints=T;let _=u.webhooks??{},E=u.webhookHooks;if(Object.keys(_).length>0){let x={};Kn({webhooks:_,hooks:E,ctx:C,webhooksTree:x,keyBuilder:u.keyBuilder}),d[u.id].webhooks=x,u.pluginWebhookMatcher&&(d[u.id].pluginWebhookMatcher=u.pluginWebhookMatcher),u.pluginTenantWebhookMatcher&&(d[u.id].pluginTenantWebhookMatcher=u.pluginTenantWebhookMatcher)}}return d}function jt(e,n,t){let r={};for(let o of e){let i=o.options,s=o.authConfig;if(i?.authType){let a=s?.[i.authType]?.integration??[],c=D({authType:i.authType,integrationName:o.id,kek:t,database:n,extraIntegrationFields:a});r[o.id]=c}}return r}import*as Kt from"querystring";var L=class extends Error{code;constructor(n,t){super(t),this.name="ConnectError",this.code=n}};function Oo(e){let n=e.oauthConfig;if(!n)throw new L("plugin_has_no_oauth_config",`Plugin '${e.id}' has no oauthConfig`);return n}async function qn(e,n){let t=P(e,()=>new L("invalid_corsair_instance","Invalid corsair instance"));if(!t.database)throw new L("no_database","No database configured on corsair instance");let r=t.connect?.redirectUri;if(!r)throw new L("no_redirect_uri","No redirectUri configured. Set connect.redirectUri in createCorsair().");let o=Ne(n,t.kek);if(!o)throw new L("invalid_state","Invalid or tampered state parameter");let{plugin:i,tenantId:s}=o,a=B(t,i,y=>new L("plugin_not_found",y)),c=Oo(a),l=await D({authType:"oauth_2",integrationName:i,kek:t.kek,database:t.database}).get_client_id();if(!l)throw new L("client_id_not_configured",`client_id not configured for '${i}'`);let u={...c.authParams,client_id:l,redirect_uri:r,response_type:"code",scope:c.scopes.join(" "),state:n},g=`${c.authUrl}?${Kt.stringify(u)}`;return{plugin:i,tenantId:s,providerName:c.providerName,oauthUrl:g,state:n}}var A=class extends Error{status;code;extra;constructor(n,t,r,o={}){super(r??t),this.name="ManagementApiError",this.status=n,this.code=t,this.extra=o}};function R(e,n){return new Response(JSON.stringify(n),{status:e,headers:{"content-type":"application/json"}})}function Zt(e){let n={error:e.code,message:e.message,...e.extra};return R(e.status,n)}function de(e){return new A(404,"not_found",e)}function te(e,n={}){return new A(400,"bad_request",e,n)}function qt(e,n){return B(e,n,t=>de(t))}async function Gn(e,n){let t=e.options?.authType;if(!t||!n.database||!n.kek)return{configured:!1,missingFields:[]};let r=D({authType:t,integrationName:e.id,kek:n.kek,database:n.database}),o=U[t].integration,i=r,s;try{s=await Promise.all(o.map(d=>i[`get_${d}`]()))}catch{s=o.map(()=>null)}let a=o.filter((d,l)=>s[l]==null),c;return t==="oauth_2"?c=!a.includes("client_id")&&!a.includes("client_secret"):c=a.length===0,{configured:c,missingFields:a}}async function Gt(e,n){let t=e.options?.authType??null,r=e.oauthConfig,{configured:o,missingFields:i}=await Gn(e,n);return{id:e.id,authType:t,configured:o,missingFields:i,oauth:r?{providerName:r.providerName,scopes:r.scopes,requiresRegisteredRedirect:!!r.requiresRegisteredRedirect}:null}}function en(){return{ok:!0}}async function nn(e){return Promise.all(e.plugins.map(n=>Gt(n,e)))}async function tn(e,n){let t=qt(e,n);return Gt(t,e)}async function zt(e,n){return e.database?(await e.database.db.selectFrom("corsair_accounts as a").innerJoin("corsair_integrations as i","i.id","a.integration_id").select(["a.id as accountId","a.dek as dek","i.name as integrationName"]).where("a.tenant_id","=",n).execute()).map(r=>({integrationName:r.integrationName,hasCredentials:!!r.dek})):[]}async function Jt(e,n){let t=await zt(e,n),r=t.filter(o=>o.hasCredentials).map(o=>o.integrationName);return{id:n,accounts:t,connectedPlugins:r}}async function rn(e){let n=new Map;if(n.set("default",{id:"default",accounts:[],connectedPlugins:[]}),e.database){let t=await e.database.db.selectFrom("corsair_accounts as a").innerJoin("corsair_integrations as i","i.id","a.integration_id").select(["a.tenant_id","a.dek as dek","i.name as integrationName"]).execute();for(let r of t){let o=r.tenant_id;if(!o)continue;let i=n.get(o);i||(i={id:o,accounts:[],connectedPlugins:[]},n.set(o,i));let s=!!r.dek;i.accounts.push({integrationName:r.integrationName,hasCredentials:s}),s&&i.connectedPlugins.push(r.integrationName)}}return[...n.values()]}async function on(e,n){if(!n)throw te("Tenant id must be a non-empty string");return Jt(e,n)}async function sn(e,n){let t=n?.id?.trim();if(!t)throw te("Tenant id is required",{missingFields:["id"]});return Jt(e,t)}async function an(e,n){let t=n?.trim()||"default",r={},o=e.database?await zt(e,t):[],i=new Map(o.map(s=>[s.integrationName,s]));for(let s of e.plugins){if(!(await Gn(s,e)).configured){r[s.id]="missing_credentials";continue}let c=i.get(s.id),d;c&&c.hasCredentials?d="connected":d="not_connected",r[s.id]=d}return r}async function cn(e,n){if(!e.database)throw de(`Permission '${n}' not found`);let t=await e.database.db.selectFrom("corsair_permissions").selectAll().where("id","=",n).executeTakeFirst();if(!t)throw de(`Permission '${n}' not found`);return t}function Do(e){if(!e.oauthConfig)throw te(`Plugin '${e.id}' has no oauthConfig`)}function zn(e){if(!e.connect)throw new A(500,"connect_not_configured","createCorsair was not given a connect config. Set { connect: { baseUrl, redirectUri } } to enable /connect routes.");return e.connect}async function un(e,n){let t=n?.plugin?.trim();if(!t)throw te("Plugin id is required",{missingFields:["plugin"]});let r=n.tenantId?.trim()||"default",o=qt(e,t);Do(o);let i=zn(e);if(!e.database||!e.kek)throw new A(500,"database_not_configured","A database and kek are required to issue connect links.");let s=await Gn(o,e);if(!s.configured)throw new A(400,"missing_credentials",`Plugin '${t}' is missing OAuth client credentials`,{missingFields:s.missingFields});let a=ae(J(t,r),e.kek),c;try{c=new URL(i.baseUrl)}catch{throw new A(500,"connect_misconfigured","connect.baseUrl is not a valid URL. Set a full URL including protocol (e.g. https://app.example.com/connect).")}return c.searchParams.set("state",a),{connectUrl:c.toString(),state:a}}async function dn(e,n,t){let r=t?.trim();if(!r)throw te("state is required",{missingFields:["state"]});zn(n);try{return await qn(e,r)}catch(o){if(o instanceof L)switch(o.code){case"invalid_state":throw te("Invalid or expired state");case"client_id_not_configured":throw new A(400,"missing_credentials","OAuth client_id is not configured for this plugin",{missingFields:["client_id"]});case"no_redirect_uri":break}throw new A(500,"resolve_failed","Could not resolve connect link. Check server logs for details.")}}async function ln(e,n,t){let r=t?.code?.trim(),o=t?.state?.trim(),i=[];if(r||i.push("code"),o||i.push("state"),i.length)throw te("Missing required fields",{missingFields:i});let s=zn(n),{processOAuthCallback:a}=await import("./oauth.js");try{return await a(e,{code:r,state:o,redirectUri:s.redirectUri})}catch(c){if(c instanceof Error&&c.name==="OAuthCallbackError")switch(c.code){case"invalid_state":throw te("Invalid or expired state");case"credentials_not_configured":throw new A(400,"missing_credentials","OAuth client credentials are not configured for this plugin",{missingFields:["client_id","client_secret"]})}throw new A(502,"oauth_callback_failed","OAuth callback did not complete. Check server logs for details.")}}async function pn(e,n){if(!e.database)throw de("Permission not found");let t=await e.database.db.selectFrom("corsair_permissions").selectAll().where("token","=",n).executeTakeFirst();if(!t)throw de("Permission not found");return t}var Vt=[{method:"GET",pattern:"/ok",handler:async()=>R(200,en())},{method:"GET",pattern:"/tenants",handler:async({internal:e})=>R(200,await rn(e))},{method:"POST",pattern:"/tenants",handler:async({internal:e,body:n})=>R(201,await sn(e,n))},{method:"GET",pattern:"/tenants/:id",handler:async({internal:e,params:n})=>R(200,await on(e,n.id))},{method:"GET",pattern:"/plugins",handler:async({internal:e})=>R(200,await nn(e))},{method:"GET",pattern:"/plugins/:id",handler:async({internal:e,params:n})=>R(200,await tn(e,n.id))},{method:"GET",pattern:"/connection-status",handler:async({internal:e,query:n})=>R(200,await an(e,n.tenantId))},{method:"GET",pattern:"/permissions/:id",handler:async({internal:e,params:n})=>R(200,await cn(e,n.id))},{method:"POST",pattern:"/permissions/lookup-by-token",handler:async({internal:e,body:n})=>{let t=n?.token?.trim();return t?R(200,await pn(e,t)):R(400,{error:"bad_request",message:"token is required",missingFields:["token"]})}},{method:"POST",pattern:"/connect/links",handler:async({internal:e,body:n})=>R(200,await un(e,n))},{method:"GET",pattern:"/connect/resolve",handler:async({corsair:e,internal:n,query:t})=>R(200,await dn(e,n,t.state??""))},{method:"POST",pattern:"/connect/oauth/callback",handler:async({corsair:e,internal:n,body:t})=>R(200,await ln(e,n,t))}];(()=>{let e=new Set;for(let n of Vt){let t=`${n.method} ${n.pattern}`;if(e.has(t))throw new Error(`Duplicate management route registered: ${t}`);e.add(t)}})();function Ho(e,n){let t=e.split("/").filter(Boolean),r=n.split("/").filter(Boolean);if(t.length!==r.length)return null;let o={};for(let i=0;i<t.length;i++){let s=t[i],a=r[i];if(s.startsWith(":"))o[s.slice(1)]=decodeURIComponent(a);else if(s!==a)return null}return o}function Mo(e,n){if(!n)return e;let t=n.endsWith("/")?n.slice(0,-1):n;return e===t?"/":e.startsWith(`${t}/`)?e.slice(t.length):e}async function $o(e){if(!(e.method==="GET"||e.method==="HEAD"||!(e.headers.get("content-type")??"").includes("application/json")))try{let t=await e.text();return t?JSON.parse(t):void 0}catch{throw new A(400,"invalid_json","Request body is not valid JSON")}}var Fo="/api/corsair";function ge(e,n={}){let t=n.basePath??Fo,r=P(e,()=>new Error("managementHandler: invalid corsair instance (missing internal config)"));return async o=>{try{let i=new URL(o.url),s=Mo(i.pathname,t),a=o.method.toUpperCase(),c=Object.fromEntries(i.searchParams);for(let d of Vt){if(d.method!==a)continue;let l=Ho(d.pattern,s);if(!l)continue;let u=await $o(o);return await d.handler({corsair:e,internal:r,req:o,params:l,query:c,body:u})}throw de(`No route for ${a} ${s}`)}catch(i){if(n.onError){let a=await n.onError(i,o);if(a)return a}if(i instanceof A)return Zt(i);let s=i instanceof Error?i.message:"Internal server error";return R(500,{error:"internal_error",message:s})}}}function Bo(e){let n=e.get?.("host")??"localhost",t=e.protocol??"http",r=e.originalUrl??e.url,o=`${t}://${n}${r}`,i=new Headers;for(let[c,d]of Object.entries(e.headers))if(d!=null)if(Array.isArray(d))for(let l of d)i.append(c,l);else i.set(c,d);let s=e.method!=="GET"&&e.method!=="HEAD",a={method:e.method,headers:i};return s&&e.body!==void 0&&(a.body=typeof e.body=="string"?e.body:JSON.stringify(e.body),i.has("content-type")||i.set("content-type","application/json")),new Request(o,a)}async function Uo(e,n){e.status(n.status),n.headers.forEach((r,o)=>e.setHeader(o,r));let t=Buffer.from(await n.arrayBuffer());e.send(t)}function Yt(e,n){let t=ge(e,n);return async(r,o,i)=>{try{let s=await t(Bo(r));await Uo(o,s)}catch(s){i(s)}}}function Qt(e,n){let t=ge(e,n);return r=>t(r.req.raw)}function Xt(e,n){let t=ge(e,n);return{GET:t,POST:t}}function er(e){let n={[Me]:e};return{ok:en,tenants:{list:()=>rn(e),create:t=>sn(e,t),get:t=>on(e,t)},plugins:{list:()=>nn(e),get:t=>tn(e,t)},connectionStatus:{get:t=>an(e,t?.tenantId)},permissions:{get:t=>cn(e,t),getByToken:t=>pn(e,t)},connect:{createLink:t=>un(e,t),resolve:t=>dn(n,e,t),oauthCallback:t=>ln(n,e,t)}}}async function nr(e,n,t,r,o="pending"){if(!e)return null;try{let i=st(),s=new Date;return await e.db.insertInto("corsair_events").values({id:i,created_at:s,updated_at:s,account_id:n,event_type:t,payload:r,status:o}).execute(),i}catch(i){return console.warn("Failed to log event:",i),null}}async function No(e,n,t,r="pending"){try{let o=await e.$getAccountId();return nr(e.database,o,n,t,r)}catch(o){return console.warn("Failed to log event:",o),null}}import*as tr from"https";import*as rr from"querystring";function gn(e,n,t,r,o){let i=new URL(r.tokenUrl),s=r.tokenAuthMethod==="basic";return new Promise((a,c)=>{let d={code:e.trim(),redirect_uri:o,grant_type:"authorization_code"};s||(d.client_id=n,d.client_secret=t);let l=rr.stringify(d),u={"Content-Type":"application/x-www-form-urlencoded","Content-Length":Buffer.byteLength(l).toString()};s&&(u.Authorization=`Basic ${Buffer.from(`${n}:${t}`).toString("base64")}`);let g=tr.request({hostname:i.hostname,...i.port?{port:Number(i.port)}:{},path:i.pathname+i.search,method:"POST",headers:u},y=>{let p="";y.on("data",f=>{p+=f}),y.on("end",()=>{if(y.statusCode!==200){c(new Error(`Token exchange failed (${y.statusCode}): ${p}`));return}try{a(JSON.parse(p))}catch{c(new Error(`Token endpoint returned non-JSON response: ${p}`))}})});g.on("error",y=>c(new Error(`Request failed: ${y.message}`))),g.write(l),g.end()})}var or=" ";function K(e){let n=e;return n._def??n.def??{}}function Z(e){let n=e.typeName;if(n)return n;let t=e.type;if(t)return`Zod${t.split("_").map(r=>r.charAt(0).toUpperCase()+r.slice(1)).join("")}`}function j(e){return e.innerType??e.schema??e.out??e.in}function sr(e,n){switch(n){case"ZodPipe":return e.in??e.innerType;case"ZodEffects":return e.schema??e.innerType;case"ZodTransform":return e.schema??e.innerType??e.in;default:return j(e)}}function ar(e){let n=e.type;return e.element??(typeof n=="string"?void 0:n)}function yn(e,n){let t=n.shape??e.shape;return typeof t=="function"?t():t}function Pe(e){return Array.isArray(e.options)?e.options:Array.isArray(e.values)?e.values:e.entries!==null&&typeof e.entries=="object"&&!Array.isArray(e.entries)?Object.values(e.entries):[]}function cr(e,n){return e.description??n.description}function Wo(e){let n=e;for(;n;){let t=K(n),r=cr(n,t);if(r)return r;let o=Z(t);if(Xn(o)||o==="ZodPipe"||o==="ZodEffects"||o==="ZodTransform"){n=sr(t,o);continue}break}}function Xn(e){return e==="ZodOptional"||e==="ZodNullable"||e==="ZodDefault"||e==="ZodCatch"}function H(e){let n=K(e),t=Z(n);switch(t){case"ZodString":return"string";case"ZodNumber":return"number";case"ZodBoolean":return"boolean";case"ZodDate":return"Date";case"ZodNull":return"null";case"ZodUnknown":case"ZodAny":return"any";case"ZodLiteral":return String(n.value??Pe(n)[0]??"unknown");case"ZodEnum":return Pe(n).map(r=>String(r)).join(" | ");case"ZodOptional":{let r=j(n);return r?H(r):"unknown"}case"ZodNullable":{let r=j(n);return`${r?H(r):"unknown"} | null`}case"ZodDefault":case"ZodCatch":{let r=j(n);return r?H(r):"unknown"}case"ZodArray":{let r=ar(n);if(!r)return"unknown[]";let o=K(r),i=Z(o)==="ZodUnion",s=H(r);return`${i?`(${s})`:s}[]`}case"ZodRecord":return"{}";case"ZodObject":{let r=yn(e,n),o=Object.entries(r);return o.length===0?"{}":`{ ${o.map(([s,a])=>{let c=Z(K(a));return`${c==="ZodOptional"||c==="ZodNullable"?s+"?":s}: ${H(a)}`}).join(", ")} }`}case"ZodUnion":return Pe(n).map(r=>H(r)).join(" | ");case"ZodIntersection":return`${H(n.left)} & ${H(n.right)}`;case"ZodPipe":case"ZodTransform":case"ZodEffects":{let r=sr(n,t);return r?H(r):"unknown"}default:return(t??"unknown").replace("Zod","").toLowerCase()}}function re(e){let n=K(e),t=Z(n),r=cr(e,n);switch(t){case"ZodString":return{kind:"string",optional:!1,description:r};case"ZodNumber":return{kind:"number",optional:!1,description:r};case"ZodBoolean":return{kind:"boolean",optional:!1,description:r};case"ZodLiteral":{let o=n.value??Pe(n)[0],i=typeof o=="string"||typeof o=="number"||typeof o=="boolean"?o:String(o??"");return{kind:"literal",optional:!1,description:r,value:i}}case"ZodEnum":{let o=Pe(n).map(i=>String(i));return{kind:"string",optional:!1,description:r,enum:o}}case"ZodOptional":{let o=j(n),i=o?re(o):{kind:"unknown",optional:!1};return{...i,optional:!0,description:r??i.description}}case"ZodNullable":{let o=j(n),i=o?re(o):{kind:"unknown",optional:!1};return{...i,optional:!0,description:r??i.description}}case"ZodDefault":case"ZodCatch":{let o=j(n);return o?{...re(o),description:r}:{kind:"unknown",optional:!1,description:r}}case"ZodArray":{let o=ar(n);return{kind:"array",optional:!1,description:r,items:o?re(o):{kind:"unknown",optional:!1}}}case"ZodObject":{let o=yn(e,n),i={};for(let[s,a]of Object.entries(o))i[s]=re(a);return{kind:"object",optional:!1,description:r,fields:i}}case"ZodRecord":return{kind:"unknown",optional:!1,description:r};case"ZodUnion":{let o=Pe(n);for(let i of o){let s=K(i);if(Z(s)==="ZodObject")return{...re(i),description:r}}return{kind:"unknown",optional:!1,description:r}}case"ZodIntersection":case"ZodPipe":case"ZodTransform":case"ZodEffects":{let o=j(n);return o?{...re(o),description:r}:{kind:"unknown",optional:!1,description:r}}default:return{kind:"unknown",optional:!1,description:r}}}function pc(e,n){let t=n.toLowerCase(),r=t.indexOf(".");if(r===-1)return null;let o=t.slice(0,r),i=t.slice(r+1),s=e.find(l=>l.id===o);if(!s)return null;let a=i;a.startsWith("api.")&&(a=a.slice(4));let c=le(s.endpointMeta,a),d=le(s.endpointSchemas,a);return!c&&!d?null:{input:d?.input?re(d.input):null,output:d?.output?re(d.output):null,description:c?.description}}var et=["equals","contains","startsWith","endsWith","in"],Lo=["equals","gt","gte","lt","lte","in"],jo=["equals"],Ko=["equals","before","after","between"];function ur(e){let n=K(e);switch(Z(n)){case"ZodOptional":case"ZodNullable":case"ZodDefault":case"ZodCatch":{let r=j(n);return r?ur(r):null}case"ZodString":return"string";case"ZodNumber":return"number";case"ZodBoolean":return"boolean";case"ZodDate":return"date";default:return null}}function nt(e){let n=K(e),t=Z(n);if(Xn(t)){let i=j(n);return i?nt(i):{}}if(t!=="ZodObject")return{};let r=yn(e,n),o={};for(let[i,s]of Object.entries(r)){let a=ur(s);a==="string"?o[i]={type:"string",operators:et}:a==="number"?o[i]={type:"number",operators:Lo}:a==="boolean"?o[i]={type:"boolean",operators:jo}:a==="date"&&(o[i]={type:"date",operators:Ko})}return o}function dr(e,n){for(let[t,r]of Object.entries(e))if(t.toLowerCase()===n)return[t,r]}function Vn(e,n,t){for(let[r,o]of Object.entries(e)){let i=[...n,r];typeof o=="function"?t.push(i.join(".")):o!==null&&typeof o=="object"&&Vn(o,i,t)}}function Yn(e){return e!==null&&typeof e=="object"&&"match"in e&&"handler"in e&&typeof e.match=="function"&&typeof e.handler=="function"}function Qn(e,n,t){for(let[r,o]of Object.entries(e)){let i=[...n,r];Yn(o)?t.push(i.join(".")):o!==null&&typeof o=="object"&&Qn(o,i,t)}}function tt(e,n){if(n.length===0)return null;let[t,...r]=n,o=Object.entries(e).find(([a])=>a.toLowerCase()===t);if(!o)return null;let[i,s]=o;if(r.length===0)return Yn(s)?[i]:null;if(s!==null&&typeof s=="object"&&!Yn(s)){let a=tt(s,r);if(a!==null)return[i,...a]}return null}function lr(e,n){let t=[];t.push(`${e}({`),t.push(" webhookHooks: {");for(let i=0;i<n.length;i++){let s=" ".repeat(i+2);t.push(`${s}${n[i]}: {`)}let r=" ".repeat(n.length+2),o=r+" ";t.push(`${r}before(ctx, args) {`),t.push(`${o}return { ctx, args };`),t.push(`${r}},`),t.push(`${r}after(ctx, response) {`),t.push(`${r}},`);for(let i=n.length-1;i>=0;i--){let s=" ".repeat(i+2);t.push(`${s}},`)}return t.push(" },"),t.push("})"),t.join(`
|
|
18
|
-
`)}var
|
|
17
|
+
Use the token to approve or deny this request.`),(typeof e.approvalMode=="function"?e.approvalMode():e.approvalMode)==="synchronous"?Nt(e.db,c,l):{result:"blocked",reason:"pending",id:c,token:d,expiresAt:u}}function Io(e){return typeof e=="function"}function So(e,n,t){let r=ae(J(e,n.tenantId??t??"default"),n.kek),o=new URL(n.baseUrl);o.searchParams.set("state",r);let i=o.toString(),s=n.onAuthMissing?n.onAuthMissing({plugin:e,connectUrl:i,state:r}):`[auth-missing:${e}] Authentication required. Direct the user to connect their account: ${i}`;return new Error(s)}function jn({endpoints:e,hooks:n,ctx:t,tree:r,pluginId:o,errorHandlers:i,currentPath:s=[],keyBuilder:a,permissionsConfig:c,endpointMeta:d,database:l,approvalConfig:u,tenantId:g,connectConfig:y,hubConfig:p}){for(let[f,h]of Object.entries(e)){let m=n?.[f];if(Io(h)){let C=m,b=[...s,f].join("."),k=async(w={})=>{let T;if(c){let v=d?.[b],{result:he,reason:q,onComplete:Ae,token:G,id:oe,expiresAt:F}=await Lt({pluginId:o,endpointPath:b,args:w,mode:c.mode,override:c.overrides?.[b],riskLevel:v?.riskLevel??"write",meta:v,db:l,timeoutMs:u?Ln(u.timeout):void 0,tenantId:g,approvalMode:u?.mode});if(he==="blocked"){let O;if(q==="denied")O=`Action '${b}' was denied by the user. Await further instructions before proceeding.`;else if(q==="policy")O=`Action '${b}' is blocked by the permission policy. Update the corsair config to allow it.`;else if(q==="timeout")O=`Action '${b}' timed out waiting for approval.`;else if(G&&oe)if(u?.formatAsyncMessage)O=u.formatAsyncMessage({token:G,id:oe,plugin:o,endpoint:b,args:w});else if(p)try{let pe=u?Ln(u.timeout):6e5,mn=await Nn(p,{permissionId:oe,permissionToken:G,plugin:o,endpoint:b,args:w,tenantId:g??"default",expiresAt:F??new Date(Date.now()+pe).toISOString()});O=Wn(mn.approvalUrl)}catch(pe){let mn=pe instanceof Error?pe.message:String(pe);O=`Action '${b}' requires user approval before it can run. Could not create approval link: ${mn}`}else O=`Action '${b}' requires user approval before it can run.`;else O=`Action '${b}' requires user approval before it can run.`;throw new Error(O)}T=Ae}let S=async(v,he,q)=>{try{return await h(he,q)}catch(Ae){if(Ae instanceof Error){let G=await Ut(Ae,o,b,typeof q=="object"&&q!==null?q:{args:q},i);if(v<(G.maxRetries||0)){let oe=v+1;console.log(`Retrying (${oe} / ${G.maxRetries})...`);let F;if(G.headersRetryAfterMs)F=G.headersRetryAfterMs;else switch(G.retryStrategy){case"exponential_backoff":F=Math.pow(2,oe-1)*1e3;break;case"exponential_backoff_jitter":let O=Math.pow(2,oe-1)*1e3,pe=(Math.random()-.5)*1e3;F=Math.max(0,O+pe);break;case"linear_1s":F=1e3;break;case"linear_2s":F=2e3;break;case"linear_3s":F=3e3;break;case"linear_4s":F=4e3;break;default:F=1e3;break}await new Promise(O=>setTimeout(O,F)),await S(oe,he,q),console.log(`[corsair:${o}:${b}] Retry strategy:`,G)}}throw Ae}},_;try{_=a?await a(t,"endpoint"):void 0}catch(v){throw y?.oauthConfig&&y.kek&&v instanceof ne&&v.authType==="oauth_2"?So(o,y,g):v}if(!C?.before&&!C?.after){let v=await S(0,{...t,key:_},w);return await T?.(),v}let E={...t,key:_},x=C.before?await C.before(E,w):{ctx:E,args:w,continue:!0,passToAfter:void 0};if(x.continue===!1)return;let ye=await S(0,x.ctx,x.args);return await C.after?.(x.ctx,ye,x.passToAfter),await T?.(),ye};r[f]=k}else if(h&&typeof h=="object"){let C={};jn({endpoints:h,hooks:m,ctx:t,tree:C,pluginId:o,errorHandlers:i,currentPath:[...s,f],keyBuilder:a,permissionsConfig:c,endpointMeta:d,database:l,approvalConfig:u,tenantId:g,connectConfig:y,hubConfig:p}),r[f]=C}}}function _o(e){return e!==null&&typeof e=="object"&&"match"in e&&"handler"in e&&typeof e.match=="function"&&typeof e.handler=="function"}function Kn({webhooks:e,hooks:n,ctx:t,webhooksTree:r,keyBuilder:o}){for(let[i,s]of Object.entries(e)){let a=n?.[i];if(_o(s)){let c=a,d=async l=>{let u=(y,p)=>s.handler(y,p),g=o?await o(t,"webhook"):void 0;return!c?.before&&!c?.after?u({...t,key:g},l):(async()=>{let y={...t,key:g},p=c.before?await c.before(y,l):{ctx:y,args:l,continue:!0,passToAfter:void 0};if(p.continue===!1)return;let f=await u(p.ctx,p.args);return f?.success===!0&&await c.after?.(p.ctx,f,p.passToAfter),f})()};r[i]={match:s.match,handler:d}}else if(s&&typeof s=="object"){let c={};Kn({webhooks:s,hooks:a,ctx:t,webhooksTree:c,keyBuilder:o}),r[i]=c}}}function Eo(e,n,t){let r=null;return async()=>{if(r)return r;if(!e)throw new Error("Database not configured");let o=await e.db.selectFrom("corsair_integrations").selectAll().where("name","=",n).executeTakeFirst();if(!o)throw new Error(`Integration "${n}" not found. Make sure to create the integration first.`);let i=await e.db.selectFrom("corsair_accounts").selectAll().where("tenant_id","=",t).where("integration_id","=",o.id).executeTakeFirst();if(!i)throw new Error(`Account not found for tenant "${t}" and integration "${n}". Make sure to create the account first.`);return r=i.id,r}}function Oo(e,n,t,r,o){return e?kn(e.db,n,t,r,o):{findByEntityId:async()=>null,findById:async()=>null,findManyByEntityIds:async()=>[],list:async()=>[],search:async()=>[],upsertByEntityId:async()=>{throw new Error("Database not configured")},deleteById:async()=>!1,deleteByEntityId:async()=>!1,count:async()=>0}}function Zn(e,n){let{database:t,tenantId:r,kek:o,rootErrorHandlers:i,approvalConfig:s,connectConfig:a,hubConfig:c}=n,d={},l={};for(let u of e)d[u.id]={},l[u.id]={};for(let u of e){let g=u.schema,y=r??"default",p=Eo(t,u.id,y);if(g?.entities){let x={};for(let[ye,v]of Object.entries(g.entities)){let he=t?kn(t.db,p,ye,g.version,v):Oo(void 0,p,ye,g.version,v);x[ye]=he}l[u.id].db=x,d[u.id].db=x}let f=u.options,h=u.authConfig,m;if(t&&o&&f?.authType){let x=h?.[f.authType]?.account??[];m=I({authType:f.authType,integrationName:u.id,tenantId:y,kek:o,database:t,extraAccountFields:x}),d[u.id].keys=m}let C={database:t,db:l[u.id]?.db??{},$getAccountId:p,...u.options?{options:u.options}:{},...m?{keys:m,authType:f?.authType}:{},tenantId:y,...c?{hub:c}:{}},b=u.endpoints??{},k=u.hooks,w={...i,...u.errorHandlers},T={},S=u.options?.permissions;jn({endpoints:b,hooks:k,ctx:C,tree:T,pluginId:u.id,errorHandlers:w,currentPath:[],keyBuilder:u.keyBuilder,permissionsConfig:S,endpointMeta:u.endpointMeta,database:t,approvalConfig:s,tenantId:r,connectConfig:a?{...a,oauthConfig:u.oauthConfig,kek:o,tenantId:y}:void 0,hubConfig:c}),Object.keys(T).length>0&&(d[u.id].api=T),C.endpoints=T;let _=u.webhooks??{},E=u.webhookHooks;if(Object.keys(_).length>0){let x={};Kn({webhooks:_,hooks:E,ctx:C,webhooksTree:x,keyBuilder:u.keyBuilder}),d[u.id].webhooks=x,u.pluginWebhookMatcher&&(d[u.id].pluginWebhookMatcher=u.pluginWebhookMatcher),u.pluginTenantWebhookMatcher&&(d[u.id].pluginTenantWebhookMatcher=u.pluginTenantWebhookMatcher)}}return d}function jt(e,n,t){let r={};for(let o of e){let i=o.options,s=o.authConfig;if(i?.authType){let a=s?.[i.authType]?.integration??[],c=D({authType:i.authType,integrationName:o.id,kek:t,database:n,extraIntegrationFields:a});r[o.id]=c}}return r}import*as Kt from"querystring";var L=class extends Error{code;constructor(n,t){super(t),this.name="ConnectError",this.code=n}};function Do(e){let n=e.oauthConfig;if(!n)throw new L("plugin_has_no_oauth_config",`Plugin '${e.id}' has no oauthConfig`);return n}async function qn(e,n){let t=P(e,()=>new L("invalid_corsair_instance","Invalid corsair instance"));if(!t.database)throw new L("no_database","No database configured on corsair instance");let r=t.connect?.redirectUri;if(!r)throw new L("no_redirect_uri","No redirectUri configured. Set connect.redirectUri in createCorsair().");let o=Ne(n,t.kek);if(!o)throw new L("invalid_state","Invalid or tampered state parameter");let{plugin:i,tenantId:s}=o,a=B(t,i,y=>new L("plugin_not_found",y)),c=Do(a),l=await D({authType:"oauth_2",integrationName:i,kek:t.kek,database:t.database}).get_client_id();if(!l)throw new L("client_id_not_configured",`client_id not configured for '${i}'`);let u={...c.authParams,client_id:l,redirect_uri:r,response_type:"code",scope:c.scopes.join(" "),state:n},g=`${c.authUrl}?${Kt.stringify(u)}`;return{plugin:i,tenantId:s,providerName:c.providerName,oauthUrl:g,state:n}}var A=class extends Error{status;code;extra;constructor(n,t,r,o={}){super(r??t),this.name="ManagementApiError",this.status=n,this.code=t,this.extra=o}};function R(e,n){return new Response(JSON.stringify(n),{status:e,headers:{"content-type":"application/json"}})}function Zt(e){let n={error:e.code,message:e.message,...e.extra};return R(e.status,n)}function de(e){return new A(404,"not_found",e)}function te(e,n={}){return new A(400,"bad_request",e,n)}function qt(e,n){return B(e,n,t=>de(t))}async function Gn(e,n){let t=e.options?.authType;if(!t||!n.database||!n.kek)return{configured:!1,missingFields:[]};let r=D({authType:t,integrationName:e.id,kek:n.kek,database:n.database}),o=U[t].integration,i=r,s;try{s=await Promise.all(o.map(d=>i[`get_${d}`]()))}catch{s=o.map(()=>null)}let a=o.filter((d,l)=>s[l]==null),c;return t==="oauth_2"?c=!a.includes("client_id")&&!a.includes("client_secret"):c=a.length===0,{configured:c,missingFields:a}}async function Gt(e,n){let t=e.options?.authType??null,r=e.oauthConfig,{configured:o,missingFields:i}=await Gn(e,n);return{id:e.id,authType:t,configured:o,missingFields:i,oauth:r?{providerName:r.providerName,scopes:r.scopes,requiresRegisteredRedirect:!!r.requiresRegisteredRedirect}:null}}function en(){return{ok:!0}}async function nn(e){return Promise.all(e.plugins.map(n=>Gt(n,e)))}async function tn(e,n){let t=qt(e,n);return Gt(t,e)}async function zt(e,n){return e.database?(await e.database.db.selectFrom("corsair_accounts as a").innerJoin("corsair_integrations as i","i.id","a.integration_id").select(["a.id as accountId","a.dek as dek","i.name as integrationName"]).where("a.tenant_id","=",n).execute()).map(r=>({integrationName:r.integrationName,hasCredentials:!!r.dek})):[]}async function Jt(e,n){let t=await zt(e,n),r=t.filter(o=>o.hasCredentials).map(o=>o.integrationName);return{id:n,accounts:t,connectedPlugins:r}}async function rn(e){let n=new Map;if(n.set("default",{id:"default",accounts:[],connectedPlugins:[]}),e.database){let t=await e.database.db.selectFrom("corsair_accounts as a").innerJoin("corsair_integrations as i","i.id","a.integration_id").select(["a.tenant_id","a.dek as dek","i.name as integrationName"]).execute();for(let r of t){let o=r.tenant_id;if(!o)continue;let i=n.get(o);i||(i={id:o,accounts:[],connectedPlugins:[]},n.set(o,i));let s=!!r.dek;i.accounts.push({integrationName:r.integrationName,hasCredentials:s}),s&&i.connectedPlugins.push(r.integrationName)}}return[...n.values()]}async function on(e,n){if(!n)throw te("Tenant id must be a non-empty string");return Jt(e,n)}async function sn(e,n){let t=n?.id?.trim();if(!t)throw te("Tenant id is required",{missingFields:["id"]});return Jt(e,t)}async function an(e,n){let t=n?.trim()||"default",r={},o=e.database?await zt(e,t):[],i=new Map(o.map(s=>[s.integrationName,s]));for(let s of e.plugins){if(!(await Gn(s,e)).configured){r[s.id]="missing_credentials";continue}let c=i.get(s.id),d;c&&c.hasCredentials?d="connected":d="not_connected",r[s.id]=d}return r}async function cn(e,n){if(!e.database)throw de(`Permission '${n}' not found`);let t=await e.database.db.selectFrom("corsair_permissions").selectAll().where("id","=",n).executeTakeFirst();if(!t)throw de(`Permission '${n}' not found`);return t}function Ho(e){if(!e.oauthConfig)throw te(`Plugin '${e.id}' has no oauthConfig`)}function zn(e){if(!e.connect)throw new A(500,"connect_not_configured","createCorsair was not given a connect config. Set { connect: { baseUrl, redirectUri } } to enable /connect routes.");return e.connect}async function un(e,n){let t=n?.plugin?.trim();if(!t)throw te("Plugin id is required",{missingFields:["plugin"]});let r=n.tenantId?.trim()||"default",o=qt(e,t);Ho(o);let i=zn(e);if(!e.database||!e.kek)throw new A(500,"database_not_configured","A database and kek are required to issue connect links.");let s=await Gn(o,e);if(!s.configured)throw new A(400,"missing_credentials",`Plugin '${t}' is missing OAuth client credentials`,{missingFields:s.missingFields});let a=ae(J(t,r),e.kek),c;try{c=new URL(i.baseUrl)}catch{throw new A(500,"connect_misconfigured","connect.baseUrl is not a valid URL. Set a full URL including protocol (e.g. https://app.example.com/connect).")}return c.searchParams.set("state",a),{connectUrl:c.toString(),state:a}}async function dn(e,n,t){let r=t?.trim();if(!r)throw te("state is required",{missingFields:["state"]});zn(n);try{return await qn(e,r)}catch(o){if(o instanceof L)switch(o.code){case"invalid_state":throw te("Invalid or expired state");case"client_id_not_configured":throw new A(400,"missing_credentials","OAuth client_id is not configured for this plugin",{missingFields:["client_id"]});case"no_redirect_uri":break}throw new A(500,"resolve_failed","Could not resolve connect link. Check server logs for details.")}}async function ln(e,n,t){let r=t?.code?.trim(),o=t?.state?.trim(),i=[];if(r||i.push("code"),o||i.push("state"),i.length)throw te("Missing required fields",{missingFields:i});let s=zn(n),{processOAuthCallback:a}=await import("./oauth.js");try{return await a(e,{code:r,state:o,redirectUri:s.redirectUri})}catch(c){if(c instanceof Error&&c.name==="OAuthCallbackError")switch(c.code){case"invalid_state":throw te("Invalid or expired state");case"credentials_not_configured":throw new A(400,"missing_credentials","OAuth client credentials are not configured for this plugin",{missingFields:["client_id","client_secret"]})}throw new A(502,"oauth_callback_failed","OAuth callback did not complete. Check server logs for details.")}}async function pn(e,n){if(!e.database)throw de("Permission not found");let t=await e.database.db.selectFrom("corsair_permissions").selectAll().where("token","=",n).executeTakeFirst();if(!t)throw de("Permission not found");return t}var Vt=[{method:"GET",pattern:"/ok",handler:async()=>R(200,en())},{method:"GET",pattern:"/tenants",handler:async({internal:e})=>R(200,await rn(e))},{method:"POST",pattern:"/tenants",handler:async({internal:e,body:n})=>R(201,await sn(e,n))},{method:"GET",pattern:"/tenants/:id",handler:async({internal:e,params:n})=>R(200,await on(e,n.id))},{method:"GET",pattern:"/plugins",handler:async({internal:e})=>R(200,await nn(e))},{method:"GET",pattern:"/plugins/:id",handler:async({internal:e,params:n})=>R(200,await tn(e,n.id))},{method:"GET",pattern:"/connection-status",handler:async({internal:e,query:n})=>R(200,await an(e,n.tenantId))},{method:"GET",pattern:"/permissions/:id",handler:async({internal:e,params:n})=>R(200,await cn(e,n.id))},{method:"POST",pattern:"/permissions/lookup-by-token",handler:async({internal:e,body:n})=>{let t=n?.token?.trim();return t?R(200,await pn(e,t)):R(400,{error:"bad_request",message:"token is required",missingFields:["token"]})}},{method:"POST",pattern:"/connect/links",handler:async({internal:e,body:n})=>R(200,await un(e,n))},{method:"GET",pattern:"/connect/resolve",handler:async({corsair:e,internal:n,query:t})=>R(200,await dn(e,n,t.state??""))},{method:"POST",pattern:"/connect/oauth/callback",handler:async({corsair:e,internal:n,body:t})=>R(200,await ln(e,n,t))}];(()=>{let e=new Set;for(let n of Vt){let t=`${n.method} ${n.pattern}`;if(e.has(t))throw new Error(`Duplicate management route registered: ${t}`);e.add(t)}})();function Mo(e,n){let t=e.split("/").filter(Boolean),r=n.split("/").filter(Boolean);if(t.length!==r.length)return null;let o={};for(let i=0;i<t.length;i++){let s=t[i],a=r[i];if(s.startsWith(":"))o[s.slice(1)]=decodeURIComponent(a);else if(s!==a)return null}return o}function $o(e,n){if(!n)return e;let t=n.endsWith("/")?n.slice(0,-1):n;return e===t?"/":e.startsWith(`${t}/`)?e.slice(t.length):e}async function Fo(e){if(!(e.method==="GET"||e.method==="HEAD"||!(e.headers.get("content-type")??"").includes("application/json")))try{let t=await e.text();return t?JSON.parse(t):void 0}catch{throw new A(400,"invalid_json","Request body is not valid JSON")}}var Bo="/api/corsair";function ge(e,n={}){let t=n.basePath??Bo,r=P(e,()=>new Error("managementHandler: invalid corsair instance (missing internal config)"));return async o=>{try{let i=new URL(o.url),s=$o(i.pathname,t),a=o.method.toUpperCase(),c=Object.fromEntries(i.searchParams);for(let d of Vt){if(d.method!==a)continue;let l=Mo(d.pattern,s);if(!l)continue;let u=await Fo(o);return await d.handler({corsair:e,internal:r,req:o,params:l,query:c,body:u})}throw de(`No route for ${a} ${s}`)}catch(i){if(n.onError){let a=await n.onError(i,o);if(a)return a}if(i instanceof A)return Zt(i);let s=i instanceof Error?i.message:"Internal server error";return R(500,{error:"internal_error",message:s})}}}function Uo(e){let n=e.get?.("host")??"localhost",t=e.protocol??"http",r=e.originalUrl??e.url,o=`${t}://${n}${r}`,i=new Headers;for(let[c,d]of Object.entries(e.headers))if(d!=null)if(Array.isArray(d))for(let l of d)i.append(c,l);else i.set(c,d);let s=e.method!=="GET"&&e.method!=="HEAD",a={method:e.method,headers:i};return s&&e.body!==void 0&&(a.body=typeof e.body=="string"?e.body:JSON.stringify(e.body),i.has("content-type")||i.set("content-type","application/json")),new Request(o,a)}async function No(e,n){e.status(n.status),n.headers.forEach((r,o)=>e.setHeader(o,r));let t=Buffer.from(await n.arrayBuffer());e.send(t)}function Yt(e,n){let t=ge(e,n);return async(r,o,i)=>{try{let s=await t(Uo(r));await No(o,s)}catch(s){i(s)}}}function Qt(e,n){let t=ge(e,n);return r=>t(r.req.raw)}function Xt(e,n){let t=ge(e,n);return{GET:t,POST:t}}function er(e){let n={[Me]:e};return{ok:en,tenants:{list:()=>rn(e),create:t=>sn(e,t),get:t=>on(e,t)},plugins:{list:()=>nn(e),get:t=>tn(e,t)},connectionStatus:{get:t=>an(e,t?.tenantId)},permissions:{get:t=>cn(e,t),getByToken:t=>pn(e,t)},connect:{createLink:t=>un(e,t),resolve:t=>dn(n,e,t),oauthCallback:t=>ln(n,e,t)}}}async function nr(e,n,t,r,o="pending"){if(!e)return null;try{let i=st(),s=new Date;return await e.db.insertInto("corsair_events").values({id:i,created_at:s,updated_at:s,account_id:n,event_type:t,payload:r,status:o}).execute(),i}catch(i){return console.warn("Failed to log event:",i),null}}async function Wo(e,n,t,r="pending"){try{let o=await e.$getAccountId();return nr(e.database,o,n,t,r)}catch(o){return console.warn("Failed to log event:",o),null}}import*as tr from"https";import*as rr from"querystring";function gn(e,n,t,r,o){let i=new URL(r.tokenUrl),s=r.tokenAuthMethod==="basic";return new Promise((a,c)=>{let d={code:e.trim(),redirect_uri:o,grant_type:"authorization_code"};s||(d.client_id=n,d.client_secret=t);let l=rr.stringify(d),u={"Content-Type":"application/x-www-form-urlencoded","Content-Length":Buffer.byteLength(l).toString()};s&&(u.Authorization=`Basic ${Buffer.from(`${n}:${t}`).toString("base64")}`);let g=tr.request({hostname:i.hostname,...i.port?{port:Number(i.port)}:{},path:i.pathname+i.search,method:"POST",headers:u},y=>{let p="";y.on("data",f=>{p+=f}),y.on("end",()=>{if(y.statusCode!==200){c(new Error(`Token exchange failed (${y.statusCode}): ${p}`));return}try{a(JSON.parse(p))}catch{c(new Error(`Token endpoint returned non-JSON response: ${p}`))}})});g.on("error",y=>c(new Error(`Request failed: ${y.message}`))),g.write(l),g.end()})}var or=" ";function K(e){let n=e;return n._def??n.def??{}}function Z(e){let n=e.typeName;if(n)return n;let t=e.type;if(t)return`Zod${t.split("_").map(r=>r.charAt(0).toUpperCase()+r.slice(1)).join("")}`}function j(e){return e.innerType??e.schema??e.out??e.in}function sr(e,n){switch(n){case"ZodPipe":return e.in??e.innerType;case"ZodEffects":return e.schema??e.innerType;case"ZodTransform":return e.schema??e.innerType??e.in;default:return j(e)}}function ar(e){let n=e.type;return e.element??(typeof n=="string"?void 0:n)}function yn(e,n){let t=n.shape??e.shape;return typeof t=="function"?t():t}function Pe(e){return Array.isArray(e.options)?e.options:Array.isArray(e.values)?e.values:e.entries!==null&&typeof e.entries=="object"&&!Array.isArray(e.entries)?Object.values(e.entries):[]}function cr(e,n){return e.description??n.description}function Lo(e){let n=e;for(;n;){let t=K(n),r=cr(n,t);if(r)return r;let o=Z(t);if(Xn(o)||o==="ZodPipe"||o==="ZodEffects"||o==="ZodTransform"){n=sr(t,o);continue}break}}function Xn(e){return e==="ZodOptional"||e==="ZodNullable"||e==="ZodDefault"||e==="ZodCatch"}function H(e){let n=K(e),t=Z(n);switch(t){case"ZodString":return"string";case"ZodNumber":return"number";case"ZodBoolean":return"boolean";case"ZodDate":return"Date";case"ZodNull":return"null";case"ZodUnknown":case"ZodAny":return"any";case"ZodLiteral":return String(n.value??Pe(n)[0]??"unknown");case"ZodEnum":return Pe(n).map(r=>String(r)).join(" | ");case"ZodOptional":{let r=j(n);return r?H(r):"unknown"}case"ZodNullable":{let r=j(n);return`${r?H(r):"unknown"} | null`}case"ZodDefault":case"ZodCatch":{let r=j(n);return r?H(r):"unknown"}case"ZodArray":{let r=ar(n);if(!r)return"unknown[]";let o=K(r),i=Z(o)==="ZodUnion",s=H(r);return`${i?`(${s})`:s}[]`}case"ZodRecord":return"{}";case"ZodObject":{let r=yn(e,n),o=Object.entries(r);return o.length===0?"{}":`{ ${o.map(([s,a])=>{let c=Z(K(a));return`${c==="ZodOptional"||c==="ZodNullable"?s+"?":s}: ${H(a)}`}).join(", ")} }`}case"ZodUnion":return Pe(n).map(r=>H(r)).join(" | ");case"ZodIntersection":return`${H(n.left)} & ${H(n.right)}`;case"ZodPipe":case"ZodTransform":case"ZodEffects":{let r=sr(n,t);return r?H(r):"unknown"}default:return(t??"unknown").replace("Zod","").toLowerCase()}}function re(e){let n=K(e),t=Z(n),r=cr(e,n);switch(t){case"ZodString":return{kind:"string",optional:!1,description:r};case"ZodNumber":return{kind:"number",optional:!1,description:r};case"ZodBoolean":return{kind:"boolean",optional:!1,description:r};case"ZodLiteral":{let o=n.value??Pe(n)[0],i=typeof o=="string"||typeof o=="number"||typeof o=="boolean"?o:String(o??"");return{kind:"literal",optional:!1,description:r,value:i}}case"ZodEnum":{let o=Pe(n).map(i=>String(i));return{kind:"string",optional:!1,description:r,enum:o}}case"ZodOptional":{let o=j(n),i=o?re(o):{kind:"unknown",optional:!1};return{...i,optional:!0,description:r??i.description}}case"ZodNullable":{let o=j(n),i=o?re(o):{kind:"unknown",optional:!1};return{...i,optional:!0,description:r??i.description}}case"ZodDefault":case"ZodCatch":{let o=j(n);return o?{...re(o),description:r}:{kind:"unknown",optional:!1,description:r}}case"ZodArray":{let o=ar(n);return{kind:"array",optional:!1,description:r,items:o?re(o):{kind:"unknown",optional:!1}}}case"ZodObject":{let o=yn(e,n),i={};for(let[s,a]of Object.entries(o))i[s]=re(a);return{kind:"object",optional:!1,description:r,fields:i}}case"ZodRecord":return{kind:"unknown",optional:!1,description:r};case"ZodUnion":{let o=Pe(n);for(let i of o){let s=K(i);if(Z(s)==="ZodObject")return{...re(i),description:r}}return{kind:"unknown",optional:!1,description:r}}case"ZodIntersection":case"ZodPipe":case"ZodTransform":case"ZodEffects":{let o=j(n);return o?{...re(o),description:r}:{kind:"unknown",optional:!1,description:r}}default:return{kind:"unknown",optional:!1,description:r}}}function gc(e,n){let t=n.toLowerCase(),r=t.indexOf(".");if(r===-1)return null;let o=t.slice(0,r),i=t.slice(r+1),s=e.find(l=>l.id===o);if(!s)return null;let a=i;a.startsWith("api.")&&(a=a.slice(4));let c=le(s.endpointMeta,a),d=le(s.endpointSchemas,a);return!c&&!d?null:{input:d?.input?re(d.input):null,output:d?.output?re(d.output):null,description:c?.description}}var et=["equals","contains","startsWith","endsWith","in"],jo=["equals","gt","gte","lt","lte","in"],Ko=["equals"],Zo=["equals","before","after","between"];function ur(e){let n=K(e);switch(Z(n)){case"ZodOptional":case"ZodNullable":case"ZodDefault":case"ZodCatch":{let r=j(n);return r?ur(r):null}case"ZodString":return"string";case"ZodNumber":return"number";case"ZodBoolean":return"boolean";case"ZodDate":return"date";default:return null}}function nt(e){let n=K(e),t=Z(n);if(Xn(t)){let i=j(n);return i?nt(i):{}}if(t!=="ZodObject")return{};let r=yn(e,n),o={};for(let[i,s]of Object.entries(r)){let a=ur(s);a==="string"?o[i]={type:"string",operators:et}:a==="number"?o[i]={type:"number",operators:jo}:a==="boolean"?o[i]={type:"boolean",operators:Ko}:a==="date"&&(o[i]={type:"date",operators:Zo})}return o}function dr(e,n){for(let[t,r]of Object.entries(e))if(t.toLowerCase()===n)return[t,r]}function Vn(e,n,t){for(let[r,o]of Object.entries(e)){let i=[...n,r];typeof o=="function"?t.push(i.join(".")):o!==null&&typeof o=="object"&&Vn(o,i,t)}}function Yn(e){return e!==null&&typeof e=="object"&&"match"in e&&"handler"in e&&typeof e.match=="function"&&typeof e.handler=="function"}function Qn(e,n,t){for(let[r,o]of Object.entries(e)){let i=[...n,r];Yn(o)?t.push(i.join(".")):o!==null&&typeof o=="object"&&Qn(o,i,t)}}function tt(e,n){if(n.length===0)return null;let[t,...r]=n,o=Object.entries(e).find(([a])=>a.toLowerCase()===t);if(!o)return null;let[i,s]=o;if(r.length===0)return Yn(s)?[i]:null;if(s!==null&&typeof s=="object"&&!Yn(s)){let a=tt(s,r);if(a!==null)return[i,...a]}return null}function lr(e,n){let t=[];t.push(`${e}({`),t.push(" webhookHooks: {");for(let i=0;i<n.length;i++){let s=" ".repeat(i+2);t.push(`${s}${n[i]}: {`)}let r=" ".repeat(n.length+2),o=r+" ";t.push(`${r}before(ctx, args) {`),t.push(`${o}return { ctx, args };`),t.push(`${r}},`),t.push(`${r}after(ctx, response) {`),t.push(`${r}},`);for(let i=n.length-1;i>=0;i--){let s=" ".repeat(i+2);t.push(`${s}},`)}return t.push(" },"),t.push("})"),t.join(`
|
|
18
|
+
`)}var qo=new Set(Fe);function fe(e,n){let t=n?.type??"api",r=n?.plugin;if(r!==void 0){let i=e.find(a=>a.id===r);if(!i)return qo.has(r)?`This plugin (${r}) is not configured. Please add it to the Corsair instance to see its associated methods.`:fe(e);if(t==="webhooks"){if(!i.webhooks)return[];let a=[];return Qn(i.webhooks,[],a),a.map(c=>`${i.id}.webhooks.${c}`)}if(t==="db"){let a=i.schema?.entities;return a?Object.keys(a).map(c=>`${i.id}.db.${c}.search`):[]}if(!i.endpoints)return[];let s=[];return Vn(i.endpoints,[],s),s.map(a=>`${i.id}.api.${a}`)}let o={};if(t==="webhooks")for(let i of e){if(!i.webhooks)continue;let s=[];Qn(i.webhooks,[],s),o[i.id]=s.map(a=>`${i.id}.webhooks.${a}`)}else if(t==="db")for(let i of e){let s=i.schema?.entities;s&&(o[i.id]=Object.keys(s).map(a=>`${i.id}.db.${a}.search`))}else for(let i of e){if(!i.endpoints)continue;let s=[];Vn(i.endpoints,[],s),o[i.id]=s.map(a=>`${i.id}.api.${a}`)}return o}function le(e,n){if(e){for(let[t,r]of Object.entries(e))if(t.toLowerCase()===n)return r}}function Go(e,n){let t=e.toLowerCase(),r=n.toLowerCase();if(!t.startsWith(`${r}.`)){let i=t.slice(n.length+1),s=i.startsWith(".")?i.slice(1):i;return s.startsWith("api.")&&(s=s.slice(4)),{shortPath:s,lookupKey:s}}let o=e.slice(n.length+1);return o.toLowerCase().startsWith("api.")&&(o=o.slice(4)),{shortPath:o,lookupKey:o.toLowerCase()}}function Jn(e,n){return typeof e=="string"?e:Array.isArray(e)?`${n}:
|
|
19
19
|
${e.join(", ")}`:`${n}:
|
|
20
20
|
`+Object.entries(e).map(([t,r])=>` ${t}: ${r.join(", ")}`).join(`
|
|
21
|
-
`)}function
|
|
21
|
+
`)}function fc(e,n){let t=n.toLowerCase(),r=t.indexOf(".");if(r!==-1){let o=t.slice(0,r),i=t.slice(r+1),s=e.find(a=>a.id===o);if(s){if(i.startsWith("db.")){let l=i.slice(3),u=l.lastIndexOf(".");if(u!==-1){let g=l.slice(0,u),y=l.slice(u+1),p=s.schema?.entities;if(y==="search"&&p){let f=dr(p,g);if(f){let[h,m]=f,C=nt(m),b=[`Search ${o} ${h} stored in the local database.`,"Pass limit and offset as numbers for pagination.","","filters {",` entity_id: string [${et.join(", ")}]`];for(let[k,w]of Object.entries(C))b.push(` ${k}?: ${w.type} [${w.operators.join(", ")}]`);return b.push("}"),b.join(`
|
|
22
22
|
`)}}}return Jn(fe(e,{type:"db"}),"Path not found. Available db operations")}if(i.startsWith("webhooks.")){let l=i.slice(9);if(s.webhooks){let u=tt(s.webhooks,l.split("."));if(u!==null){let g=u.join("."),y=le(s.webhookSchemas,g.toLowerCase()),p=y?.response?H(y.response):null,f=[];return y?.description&&f.push(y.description),y?.payload&&f.push(`payload ${fn(xe(y.payload))}`),p&&f.push(`response: ${p}`),f.push(`usage:
|
|
23
23
|
${lr(o,u)}`),f.join(`
|
|
24
24
|
|
|
25
25
|
`)}}return Jn(fe(e,{type:"webhooks"}),"Path not found. Available webhooks")}let a=i;a.startsWith("api.")&&(a=a.slice(4));let c=le(s.endpointMeta,a),d=le(s.endpointSchemas,a);if(c||d){let l=[],u=[c?.riskLevel?`[${c.riskLevel}]`:"",c?.irreversible?"[irreversible]":""].filter(Boolean).join(" "),g=[c?.description,u].filter(Boolean).join(" ");return g&&l.push(g),d?.input&&l.push(`input ${fn(xe(d.input))}`),d?.output&&l.push(`output ${fn(xe(d.output))}`),l.join(`
|
|
26
26
|
|
|
27
|
-
`)}}}return Jn(fe(e),"Path not found. Available operations")}function ir(e){let n=e;for(;;){let t=K(n),r=Z(t);if(Xn(r)){let o=j(t);if(!o)return n;n=o;continue}return n}}function xe(e){if(e===void 0)return{kind:"inline",type:"unknown"};let n=ir(e),t=K(n);if(Z(t)==="ZodObject"){let o=yn(n,t),i=[];for(let[s,a]of Object.entries(o)){let c=K(a),d=Z(c),l=d==="ZodOptional"||d==="ZodNullable",u=ir(a),g=
|
|
27
|
+
`)}}}return Jn(fe(e),"Path not found. Available operations")}function ir(e){let n=e;for(;;){let t=K(n),r=Z(t);if(Xn(r)){let o=j(t);if(!o)return n;n=o;continue}return n}}function xe(e){if(e===void 0)return{kind:"inline",type:"unknown"};let n=ir(e),t=K(n);if(Z(t)==="ZodObject"){let o=yn(n,t),i=[];for(let[s,a]of Object.entries(o)){let c=K(a),d=Z(c),l=d==="ZodOptional"||d==="ZodNullable",u=ir(a),g=Lo(a);i.push({key:s,optional:l,type:H(u),...g!==void 0?{description:g}:{}})}return{kind:"object",fields:i}}return{kind:"inline",type:H(n)}}function fn(e,n=0){if(e===void 0)return"{}";if(e.kind==="inline")return e.type;if(e.kind==="object"){if(e.fields.length===0)return"{}";let t=or.repeat(n+1),r=or.repeat(n);return`{
|
|
28
28
|
${e.fields.map(i=>{let s=i.optional?`${i.key}?`:i.key,a=i.description?` // ${i.description}`:"";return`${t}${s}: ${i.type}${a}`}).join(`
|
|
29
29
|
`)}
|
|
30
|
-
${r}}`}return"unknown"}function
|
|
30
|
+
${r}}`}return"unknown"}function zo(e,n){let t=fe(e,{plugin:n,type:"api"});if(typeof t=="string")return{ok:!1,error:t};if(!Array.isArray(t))return{ok:!1,error:"list_operations did not return a path array \u2014 pass a configured plugin id."};let r=e.find(l=>l.id===n);if(!r)return{ok:!1,error:`Plugin "${n}" is not configured on this instance.`};let o=[];for(let l of t){let{shortPath:u,lookupKey:g}=Go(l,n),y=le(r.endpointMeta,g),p=le(r.endpointSchemas,g);!y&&!p||o.push({path:l,shortPath:u,description:y?.description,riskLevel:y?.riskLevel,irreversible:y?.irreversible,input:xe(p?.input),output:xe(p?.output)})}o.sort((l,u)=>l.path.localeCompare(u.path));let i=[],s=fe(e,{plugin:n,type:"webhooks"});if(Array.isArray(s)&&r.webhooks)for(let l of s){let g=l.toLowerCase().slice(n.length+1),y=g.startsWith(".")?g.slice(1):g;if(!y.startsWith("webhooks."))continue;let p=y.slice(9),f=tt(r.webhooks,p.split("."));if(f===null)continue;let h=f.join("."),m=le(r.webhookSchemas,h.toLowerCase()),C=m?.response?H(m.response):void 0;i.push({path:l,description:m?.description,payload:xe(m?.payload),responseType:C,usageExample:lr(n,f)})}i.sort((l,u)=>l.path.localeCompare(u.path));let a=[],c=fe(e,{plugin:n,type:"db"}),d=r.schema?.entities;if(Array.isArray(c)&&d)for(let l of c){let g=l.toLowerCase().slice(n.length+1),y=g.startsWith(".")?g.slice(1):g;if(!y.startsWith("db."))continue;let p=y.slice(3),f=p.lastIndexOf(".");if(f===-1)continue;let h=p.slice(0,f);if(p.slice(f+1)!=="search")continue;let C=dr(d,h);if(!C)continue;let[b,k]=C,w=nt(k),T=Object.entries(w).map(([S,_])=>({field:S,type:_.type,operators:_.operators}));a.push({path:l,entityName:b,filters:[{field:"entity_id",type:"string",operators:et},...T]})}return a.sort((l,u)=>l.path.localeCompare(u.path)),{ok:!0,data:{pluginId:n,api:o,webhooks:i,db:a}}}function pr(e,n){for(let[t,r]of Object.entries(e))if(r?.pluginWebhookMatcher&&r.pluginWebhookMatcher(n))return t;return null}function Jo(e,n){let t=pr(e,n);if(!t)return null;let r=e[t];if(!r?.pluginTenantWebhookMatcher)return null;let o=r.pluginTenantWebhookMatcher(n);return o?{plugin:t,tenantMatch:o}:null}function Vo(e){let n={};for(let t of e)!t.pluginWebhookMatcher&&!t.pluginTenantWebhookMatcher||(n[t.id]={pluginWebhookMatcher:t.pluginWebhookMatcher,pluginTenantWebhookMatcher:t.pluginTenantWebhookMatcher});return n}function gr(e,n){let t=n.toLowerCase(),r=e[t]??e[n];return Array.isArray(r)?r[0]:typeof r=="string"?r:void 0}function fr(e){if(typeof e=="string"){let n=e.trim();return n.length>0?n:void 0}if(typeof e=="number"&&Number.isFinite(e))return String(e)}function $e(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function Yo(e){return $e(e.body)}function yr(e,n){let t=e.query;if(!t)return;let r=t[n]??t[n.toLowerCase()];return hn(Array.isArray(r)?r:[r])}function hr(e){let n=e.headers??{},t=[n.validationtoken,n.validationToken,n["validation-token"],n["ms-validation-token"]];for(let c of t){let d=hn(Array.isArray(c)?c:[c]);if(d)return decodeURIComponent(d)}let r=yr({query:e.query},"validationToken");if(r)return r;let o=["x-forwarded-uri","x-original-uri","x-rewrite-url","x-envoy-original-path","referer"];for(let c of o){let d=n[c],l=Array.isArray(d)?d[0]:d;if(!(!l||typeof l!="string"))try{let g=(l.startsWith("http")?new URL(l):new URL(`https://example.invalid${l.startsWith("/")?l:`/${l}`}`)).searchParams.get("validationToken");if(g?.trim())return g.trim()}catch{continue}}let i=e.payload!==void 0?e.payload:e.body!==void 0?e.body:void 0,s=$e(typeof i=="string"?(()=>{try{return JSON.parse(i)}catch{return i}})():i);return hn([s?.validationToken])??null}function Qo(e){if(hr(e))return!0;let n=$e(e.body??e.payload);return gr(e.headers??{},"content-type")?.includes("text/plain")?!n||Object.keys(n).length===0:!1}function hn(e){for(let n of e){let t=fr(n);if(t)return t}}function Xo(e){let t=$e(e.message)?.data;if(typeof t!="string")return null;try{return JSON.parse(Buffer.from(t,"base64").toString("utf8"))}catch{return null}}var Me=Symbol.for("corsair:internal");function kt(e){let n=e.database?rt(e.database):void 0,t=n&&e.kek?jt(e.plugins,n,e.kek):Xe(!!n,!!e.kek),r={plugins:e.plugins,database:n,kek:e.kek,multiTenancy:!!e.multiTenancy,approval:e.approval,connect:e.connect,hub:e.hub?Be(e.hub):void 0},o=Wt(n),i=er(r);if(e.multiTenancy)return Object.assign({withTenant:a=>{if(!a)throw new Error("corsair.withTenant(tenantId): tenantId must be a non-empty string");let c=Zn(e.plugins,{database:n,tenantId:a,kek:e.kek,rootErrorHandlers:e.errorHandlers,approvalConfig:e.approval,connectConfig:e.connect,hubConfig:r.hub});return Object.assign(c,{[Me]:r})},keys:t,permissions:o,manage:i},{[Me]:r});let s=Zn(e.plugins,{database:n,tenantId:void 0,kek:e.kek,rootErrorHandlers:e.errorHandlers,approvalConfig:e.approval,connectConfig:e.connect,hubConfig:r.hub});return Object.assign({},s,{keys:t,permissions:o,manage:i,[Me]:r})}var $=class extends Error{code;constructor(n,t){super(t),this.name="OAuthCallbackError",this.code=n}};function kr(e){let n=e.oauthConfig;if(!n)throw new $("plugin_has_no_oauth_config",`Plugin '${e.id}' has no oauthConfig`);return n}async function ei(e,n,t,r){let o=ct(e),i=await o.integrations.findByName(n);if(!i)throw new Error(`Integration '${n}' not found. Run setupCorsair first.`);if(await o.accounts.findOne({tenant_id:t,integration_id:i.id}))return;let a=Q(),c=await X(a,r);await o.accounts.create({tenant_id:t,integration_id:i.id,config:{},dek:c})}async function Tn(e,n,t){let{tenantId:r,redirectUri:o}=t,i=P(e,()=>new $("invalid_corsair_instance","Invalid corsair instance"));if(!i.database)throw new Error("No database configured on corsair instance");let s=B(i,n,g=>new $("plugin_not_found",g)),a=kr(s),d=await D({authType:"oauth_2",integrationName:n,kek:i.kek,database:i.database}).get_client_id();if(!d)throw new Error(`client_id not configured for '${n}'`);let l=ae(J(n,r),i.kek),u={...a.authParams,client_id:d,redirect_uri:o,response_type:"code",scope:a.scopes.join(" "),state:l};return{url:`${a.authUrl}?${mr.stringify(u)}`,state:l}}async function He(e,n){let{code:t,state:r,redirectUri:o}=n,i=P(e,()=>new $("invalid_corsair_instance","Invalid corsair instance")),s=Ne(r,i.kek);if(!s)throw new $("invalid_state","Invalid or tampered state parameter");let{plugin:a,tenantId:c}=s;if(!i.database)throw new $("no_database","No database configured on corsair instance");let d=B(i,a,h=>new $("plugin_not_found",h)),l=kr(d),u=D({authType:"oauth_2",integrationName:a,kek:i.kek,database:i.database}),g=await u.get_client_id(),y=await u.get_client_secret();if(!g||!y)throw new $("credentials_not_configured",`Credentials not configured for '${a}'`);await ei(i.database,a,c,i.kek);let p=await gn(t,g,y,l,o);if(!p.access_token)throw new $("no_access_token",`No access_token returned from ${l.providerName}`);let f=I({authType:"oauth_2",integrationName:a,tenantId:c,kek:i.kek,database:i.database});await f.set_access_token(p.access_token),p.refresh_token&&await f.set_refresh_token(p.refresh_token),p.expires_in&&await f.set_expires_at(String(Math.floor(Date.now()/1e3)+p.expires_in));try{let h=await Ke(i.plugins,a,p);if(h)try{let m=d.authConfig?.oauth_2?.account??[];await Ce({database:i.database,kek:i.kek,pluginId:a,tenantId:c,link:h,authType:"oauth_2",extraAccountFields:m})}catch(m){console.warn(`[corsair:oauth] Failed to persist webhook tenant link for '${a}' tenant '${c}':`,m)}}catch(h){console.warn(`[corsair:oauth] Failed to resolve webhook tenant link for '${a}' tenant '${c}':`,h)}return{plugin:a,tenantId:c}}export{ut as a,ve as b,Ie as c,V as d,Be as e,M as f,Ue as g,J as h,bn as i,Q as j,X as k,W as l,vn as m,Oe as n,ce as o,ee as p,De as q,D as r,I as s,St as t,_t as u,Ce as v,Sn as w,ze as x,Ot as y,$ as z,Tn as A,He as B,_e as C,ke as D,ft as E,be as F,ht as G,Kr as H,Le as I,Pn as J,xn as K,An as L,Rn as M,je as N,ue as O,Te as P,Ht as Q,Je as R,Ve as S,Ye as T,On as U,Dn as V,Hn as W,Mn as X,$n as Y,Fn as Z,Bn as _,Qe as $,ne as aa,Un as ba,Ft as ca,Bt as da,qn as ea,ge as fa,Yt as ga,Qt as ha,Xt as ia,nr as ja,Wo as ka,gn as la,gc as ma,fe as na,fc as oa,fn as pa,zo as qa,pr as ra,Jo as sa,Vo as ta,gr as ua,fr as va,$e as wa,Yo as xa,yr as ya,hr as za,Qo as Aa,hn as Ba,Xo as Ca,Me as Da,kt as Ea};
|
package/dist/core.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Aa as F,Ba as G,Ca as H,Da as I,Ea as J,a,aa as p,b,ea as q,j as d,ja as r,k as e,ka as s,l as f,la as t,m as g,n as h,o as i,p as j,pa as u,q as k,qa as v,r as l,ra as w,s as m,sa as x,t as n,ta as y,u as o,ua as z,va as A,wa as B,xa as C,ya as D,za as E}from"./chunk-
|
|
1
|
+
import{Aa as F,Ba as G,Ca as H,Da as I,Ea as J,a,aa as p,b,ea as q,j as d,ja as r,k as e,ka as s,l as f,la as t,m as g,n as h,o as i,p as j,pa as u,q as k,qa as v,r as l,ra as w,s as m,sa as x,t as n,ta as y,u as o,ua as z,va as A,wa as B,xa as C,ya as D,za as E}from"./chunk-GJ743ML7.js";import{g as c}from"./chunk-6D4UDUPJ.js";import"./chunk-IGGCNGU2.js";import"./chunk-QAIKSQAD.js";export{p as AuthMissingError,c as BASE_AUTH_FIELDS,I as CORSAIR_INTERNAL,a as ProviderDisplayNames,B as asRecord,y as collectPluginWebhookMatchers,m as createAccountKeyManager,J as createCorsair,l as createIntegrationKeyManager,H as decodePubSubData,j as decryptConfig,f as decryptDEK,h as decryptWithDEK,i as encryptConfig,e as encryptDEK,g as encryptWithDEK,t as exchangeCodeForTokens,E as extractMicrosoftGraphValidationToken,G as firstString,u as formatDocSchemaShape,b as formatProviderDisplayName,d as generateDEK,z as getHeader,o as initializeAccountDEK,n as initializeIntegrationDEK,v as introspectPluginForDocs,F as isMicrosoftGraphValidationHandshake,r as logEvent,s as logEventFromContext,w as matchWebhookPlugin,x as matchWebhookPluginAndTenant,k as reEncryptConfig,C as readBodyRecord,D as readQueryParam,q as resolveConnectLink,A as toExternalId};
|
package/dist/hub.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as x,C as g,D as h,E as i,F as j,I as k,J as l,K as m,L as n,M as o,N as p,O as q,P as r,W as s,X as t,Y as u,Z as v,_ as w,b as a,ba as y,c as b,ca as z,d as c,da as A,e as d,f as e,g as f}from"./chunk-
|
|
1
|
+
import{$ as x,C as g,D as h,E as i,F as j,I as k,J as l,K as m,L as n,M as o,N as p,O as q,P as r,W as s,X as t,Y as u,Z as v,_ as w,b as a,ba as y,c as b,ca as z,d as c,da as A,e as d,f as e,g as f}from"./chunk-GJ743ML7.js";import"./chunk-6D4UDUPJ.js";import"./chunk-IGGCNGU2.js";import"./chunk-QAIKSQAD.js";export{b as DEFAULT_HUB_API_URL,c as HubNotConfiguredError,q as ManagedOAuthDeliveryError,z as attachManagedRefreshAuth,k as createHubConnectSession,A as createHubRouteHandlers,a as formatProviderDisplayName,e as getHubConfig,y as getManagedAccessToken,n as handleHubConnectSessionRequest,s as handleHubDeliveryGet,t as handleHubDeliveryPost,v as handleHubDeliveryRequest,u as hubDeliveryToResponse,g as isLoopbackDeliveryUrl,d as normalizeHubConfig,l as parseHubConnectSessionBody,m as parseHubConnectSessionSearchParams,r as processManagedOAuthDelivery,h as resolveConnectSourceFromDeliveryUrl,f as resolveHubOAuthCallbackUrl,o as respondToHubConnectSession,p as respondToHubConnectSessionFromRequest,w as respondToHubDelivery,x as respondToHubDeliveryFromRequest,i as shouldUseBrowserConnectDelivery,j as validateExplicitConnectSource};
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{Ba as H,Ca as j,Da as k,Ea as B,G as w,Q as I,V as T,aa as S,ea as O,fa as L,ga as A,ha as v,ia as $,ma as d,na as g,oa as C,pa as f,ra as E,sa as M,ta as W,ua as F,v as P,va as _,w as x,wa as N,x as b,xa as D,y as R}from"./chunk-
|
|
1
|
+
import{Ba as H,Ca as j,Da as k,Ea as B,G as w,Q as I,V as T,aa as S,ea as O,fa as L,ga as A,ha as v,ia as $,ma as d,na as g,oa as C,pa as f,ra as E,sa as M,ta as W,ua as F,v as P,va as _,w as x,wa as N,x as b,xa as D,y as R}from"./chunk-GJ743ML7.js";import"./chunk-6D4UDUPJ.js";import"./chunk-IGGCNGU2.js";import"./chunk-QAIKSQAD.js";var c=class extends Error{status;code;extra;constructor(r,o,e,a={}){super(e),this.name="CorsairClientError",this.status=r,this.code=o,this.extra=a}};function J(n){return n.endsWith("/")?n.slice(0,-1):n}async function y(n){let r={};try{r=await n.json()}catch{}let o=typeof r.error=="string"?r.error:"request_failed",e=typeof r.message=="string"?r.message:`Request failed (${n.status})`,{error:a,message:u,...t}=r;return new c(n.status,o,e,t)}function U(n){let r=J(n.baseURL),o=n.fetch??globalThis.fetch.bind(globalThis);async function e(t,s){let i=s&&Object.keys(s).length?`?${new URLSearchParams(s).toString()}`:"",p=await o(`${r}${t}${i}`,{method:"GET"});if(!p.ok)throw await y(p);return await p.json()}async function a(t,s){let i=await o(`${r}${t}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!i.ok)throw await y(i);return await i.json()}let u=encodeURIComponent;return{ok:()=>e("/ok"),tenants:{list:()=>e("/tenants"),create:t=>a("/tenants",t),get:t=>e(`/tenants/${u(t)}`)},plugins:{list:()=>e("/plugins"),get:t=>e(`/plugins/${u(t)}`)},connectionStatus:{get:t=>{let s={};return t?.tenantId&&(s.tenantId=t.tenantId),e("/connection-status",s)}},permissions:{get:t=>e(`/permissions/${u(t)}`),getByToken:t=>a("/permissions/lookup-by-token",{token:t})},connect:{createLink:t=>a("/connect/links",t),resolve:t=>e("/connect/resolve",{state:t}),oauthCallback:t=>a("/connect/oauth/callback",t)}}}function l(n){let r=n[k];if(!r)throw new Error("listOperations / getSchema: invalid corsair instance. Pass the value returned by createCorsair() or corsair.withTenant().");return r.plugins}function q(n,r){let o=g(l(n),r);return typeof o=="string"?o:Array.isArray(o)?o.join(`
|
|
2
2
|
`):Object.values(o).flat().join(`
|
|
3
3
|
`)}function G(n,r){return C(l(n),r)}function Y(n,r){return d(l(n),r)}var z=Symbol.for("corsair:internal");function K(n,r){let o=n;for(let e of r){if(!o||typeof o!="object")return null;o=o[e]}return typeof o=="function"?o:null}function h(n){return n[z]?.database}async function Q(n,r){let o=new Date().toISOString(),e=await n.permissions.find_by_token(r);if(!e)return console.error("executePermission: no permission found for token."),{error:"executePermission: no permission found for token."};if(e.status!=="approved")return console.error(`executePermission: permission '${e.id}' is '${e.status}', expected 'approved'.`),{endpoint:e.endpoint,plugin:e.plugin,result:null,error:`executePermission: permission '${e.id}' is '${e.status}', expected 'approved'.`};if(e.expires_at<o){let i=h(n);return i&&await i.db.updateTable("corsair_permissions").set({status:"expired",updated_at:new Date}).where("id","=",e.id).execute(),console.error(`executePermission: permission '${e.id}' has expired.`),{error:`executePermission: permission '${e.id}' has expired.`,endpoint:e.endpoint,plugin:e.plugin,result:null}}let a=e.tenant_id??"default",t=(n.withTenant?n.withTenant(a):n)[e.plugin];if(!t?.api)return console.error(`executePermission: plugin '${e.plugin}' not found or has no API on this corsair instance.`),{error:`executePermission: plugin '${e.plugin}' not found or has no API on this corsair instance.`,plugin:e.plugin,endpoint:e.endpoint,result:null};let s=K(t.api,e.endpoint.split("."));if(!s)return console.error(`executePermission: endpoint '${e.endpoint}' not found in plugin '${e.plugin}'.`),{endpoint:e.endpoint,plugin:e.plugin,result:null,error:`executePermission: endpoint '${e.endpoint}' not found in plugin '${e.plugin}'.`};await n.permissions.set_executing(e.id);try{let i=typeof e.args=="string"?JSON.parse(e.args):e.args,p=await s(i);return await n.permissions.set_completed(e.id),{plugin:e.plugin,endpoint:e.endpoint,result:p}}catch(i){let p=i instanceof Error?i.message:String(i),m=h(n);return m&&await m.db.updateTable("corsair_permissions").set({status:"failed",error:p,updated_at:new Date}).where("id","=",e.id).execute(),{plugin:e.plugin,endpoint:e.endpoint,result:null,error:p}}}export{S as AuthMissingError,c as CorsairClientError,N as asRecord,W as collectPluginWebhookMatchers,B as createCorsair,U as createCorsairClient,j as decodePubSubData,Q as executePermission,H as firstString,f as formatDocSchemaShape,F as getHeader,G as getSchema,Y as getStructuredSchema,q as listOperations,L as managementHandler,E as matchWebhookPlugin,M as matchWebhookPluginAndTenant,T as processCorsair,I as processWebhook,D as readBodyRecord,x as resolveAccountFromWebhookLink,O as resolveConnectLink,R as resolveTenantFromWebhookLink,b as resolveTenantIdFromWebhookLink,P as setWebhookTenantLink,w as setupCorsair,A as toExpressHandler,_ as toExternalId,v as toHonoHandler,$ as toNextJsHandler};
|
package/dist/oauth.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{A as d,B as e,h as a,i as b,z as c}from"./chunk-
|
|
1
|
+
import{A as d,B as e,h as a,i as b,z as c}from"./chunk-GJ743ML7.js";import"./chunk-6D4UDUPJ.js";import"./chunk-IGGCNGU2.js";import"./chunk-QAIKSQAD.js";export{c as OAuthCallbackError,b as decodeOAuthState,a as encodeOAuthState,d as generateOAuthUrl,e as processOAuthCallback};
|
package/dist/setup.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ interface SetupCorsairOptions {
|
|
|
25
25
|
credentials?: SetupCredentials;
|
|
26
26
|
/**
|
|
27
27
|
* When true, calls list endpoints for every plugin defined in
|
|
28
|
-
* setup/backfill.
|
|
28
|
+
* setup/backfill.config.ts to seed the local database with initial data.
|
|
29
29
|
*/
|
|
30
30
|
backfill?: boolean;
|
|
31
31
|
/**
|
|
@@ -56,7 +56,7 @@ type SetupTenantScope = {
|
|
|
56
56
|
* 3. Checks auth status for each plugin and logs guidance for any missing credentials.
|
|
57
57
|
* When `caller` is 'cli', guidance is printed as CLI flags instead of JS calls.
|
|
58
58
|
* 4. If `{ backfill: true }`, calls the list endpoints defined in
|
|
59
|
-
* `setup/backfill.
|
|
59
|
+
* `setup/backfill.config.ts` for each plugin that has auth configured.
|
|
60
60
|
*
|
|
61
61
|
* To set credentials, use the corsair client API directly after setup:
|
|
62
62
|
* - Integration-level (shared across all tenants): `corsair.keys.plugin.set_*(value)`
|
package/dist/setup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{G as a,H as b}from"./chunk-
|
|
1
|
+
import{G as a,H as b}from"./chunk-GJ743ML7.js";import"./chunk-6D4UDUPJ.js";import"./chunk-IGGCNGU2.js";import"./chunk-QAIKSQAD.js";export{b as applySetupCredentials,a as setupCorsair};
|
package/dist/tunnel.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{R as e,S as f,T as g,U as h,V as i,v as a,w as b,x as c,y as d}from"./chunk-
|
|
1
|
+
import{R as e,S as f,T as g,U as h,V as i,v as a,w as b,x as c,y as d}from"./chunk-GJ743ML7.js";import"./chunk-6D4UDUPJ.js";import"./chunk-IGGCNGU2.js";import"./chunk-QAIKSQAD.js";export{h as applyPermissionDecision,f as isManagedBrowserDelivery,e as isPermissionBrowserDelivery,i as processCorsair,b as resolveAccountFromWebhookLink,d as resolveTenantFromWebhookLink,c as resolveTenantIdFromWebhookLink,a as setWebhookTenantLink,g as verifyBrowserDeliveryToken};
|