UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

21 lines 4.62 kB
import{endpointForProtocol}from"../connections/catalog.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{patchPackageJson}from"./package-json.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const DEFAULT_CONNECT_PACKAGE_VERSION=`0.4.2`,CONNECT_PACKAGE_NAME=`@vercel/connect`,USER_AUTHORED_CONNECTION_DIR=`agent/connections`;function resolveAuth(e){return e.auth??{kind:`none`}}function envKeysForAuth(e){switch(e.kind){case`bearer-env`:return[e.envVar];case`header`:return e.headers.map(e=>e.envVar);case`connect`:case`none`:return[]}}function authBlock(e){switch(e.kind){case`connect`:return` auth: connect("${e.connector}"),\n`;case`bearer-env`:return` auth: { getToken: async () => ({ token: process.env.${e.envVar}! }) },\n`;case`header`:return` headers: () => ({\n${e.headers.map(e=>` "${e.header}": process.env.${e.envVar}!,`).join(` `)}\n }),\n`;case`none`:return``}}function renderMcpTemplate(e,t,n){return`${n.kind===`connect`?`import { connect } from "@vercel/connect/eve"; import { defineMcpClientConnection } from "eve/connections"; `:`import { defineMcpClientConnection } from "eve/connections"; `} export default defineMcpClientConnection({ url: "${e.url}", description: "${t}", ${authBlock(n)}}); `}function renderOpenApiTemplate(e,t,n){let r=n.kind===`connect`?`import { connect } from "@vercel/connect/eve"; import { defineOpenAPIConnection } from "eve/connections"; `:`import { defineOpenAPIConnection } from "eve/connections"; `,i=e.baseUrl?` baseUrl: "${e.baseUrl}",\n`:``;return`${r} export default defineOpenAPIConnection({ spec: "${e.spec}", ${i} description: "${t}", ${authBlock(n)}}); `}function renderTemplate(e,t,n,r){return e===`mcp`?renderMcpTemplate(t,n,r):renderOpenApiTemplate(t,n,r)}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function ensureConnectDependency(e,t){if(!await pathExists(e))return[];let n=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(n)&&isJsonObject(n.dependencies)?n.dependencies[CONNECT_PACKAGE_NAME]:void 0)===t?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:t}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}async function ensureConnectionDependencies(e){let t=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);return ensureConnectDependency(join(e.projectRoot,`package.json`),t)}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,t){if(t.length===0)return[];let n=``;await pathExists(e)&&(n=await readFile(e,`utf8`));let i=t.filter(e=>!envKeyPresent(n,e));if(i.length===0)return[];let a=`${n.length>0&&!n.endsWith(` `)?` `:``}${i.map(e=>`${e}=`).join(` `)}\n`;return await writeFile(e,n+a,`utf8`),i}async function ensureConnection(t){let n=t.slug??t.entry.slug,o=resolveAuth(t.entry),s=endpointForProtocol(t.entry,t.protocol);if(s===null)throw Error(`Connection "${n}" is missing a ${t.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${t.protocol}".`);let c=join(t.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${n}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!t.force&&u)return{slug:n,protocol:t.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,t.connectPackageVersion??DEFAULT_CONNECT_PACKAGE_VERSION);d.push(...await ensureConnectDependency(join(t.projectRoot,`package.json`),e))}await writeTextFile(c,renderTemplate(t.protocol,s,t.entry.description,o),{force:!0});let f=await seedEnvPlaceholders(join(t.projectRoot,`.env.local`),l),p={slug:n,protocol:t.protocol,action:u?`overwritten`:`created`,filePath:c,filesWritten:[c],filesSkipped:[],packageJsonUpdated:d,envKeysAdded:f,envKeysRequired:l};return u&&(p.filesOverwritten=[c]),p}async function listAuthoredConnections(e){let r=join(e,USER_AUTHORED_CONNECTION_DIR),i;try{i=await readdir(r,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let a=[];for(let e of i){if(e.isFile()){let n=getSupportedModuleBaseName(e.name);n!==null&&a.push(n);continue}if(e.isDirectory())try{(await readdir(join(r,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&a.push(e.name)}catch{}}return a.sort()}export{ensureConnection,ensureConnectionDependencies,listAuthoredConnections};