UNPKG

eve

Version:

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

21 lines 4.53 kB
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{endpointForProtocol}from"../connections/catalog.js";import{readFile,readdir,writeFile}from"node:fs/promises";import{join}from"node:path";const 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??`2.2.0`);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 r=t.filter(e=>!envKeyPresent(n,e));if(r.length===0)return[];let i=`${n.length>0&&!n.endsWith(` `)?` `:``}${r.map(e=>`${e}=`).join(` `)}\n`;return await writeFile(e,n+i,`utf8`),r}async function ensureConnection(e){let t=e.slug??e.entry.slug,n=resolveAuth(e.entry),r=endpointForProtocol(e.entry,e.protocol);if(r===null)throw Error(`Connection "${t}" is missing a ${e.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${e.protocol}".`);let i=join(e.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${t}.ts`),a=envKeysForAuth(n),o=await pathExists(i);if(!e.force&&o)return{slug:t,protocol:e.protocol,action:`skipped`,filePath:i,filesWritten:[],filesSkipped:[i],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:a};let s=[];if(n.kind===`connect`){let t=resolveVersionToken(`connectPackageVersion`,e.connectPackageVersion??`2.2.0`);s.push(...await ensureConnectDependency(join(e.projectRoot,`package.json`),t))}await writeTextFile(i,renderTemplate(e.protocol,r,e.entry.description,n),{force:!0});let c=await seedEnvPlaceholders(join(e.projectRoot,`.env.local`),a),l={slug:t,protocol:e.protocol,action:o?`overwritten`:`created`,filePath:i,filesWritten:[i],filesSkipped:[],packageJsonUpdated:s,envKeysAdded:c,envKeysRequired:a};return o&&(l.filesOverwritten=[i]),l}async function listAuthoredConnections(e){let t=join(e,USER_AUTHORED_CONNECTION_DIR),n;try{n=await readdir(t,{withFileTypes:!0})}catch(e){if(e.code===`ENOENT`)return[];throw e}let r=[];for(let e of n){if(e.isFile()){let t=getSupportedModuleBaseName(e.name);t!==null&&r.push(t);continue}if(e.isDirectory())try{(await readdir(join(t,e.name))).some(e=>matchesSupportedModuleBaseName(e,`connection`))&&r.push(e.name)}catch{}}return r.sort()}export{ensureConnection,ensureConnectionDependencies,listAuthoredConnections};