eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
21 lines • 4.3 kB
JavaScript
import{endpointForProtocol}from"../connections/catalog.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"./package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{join}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";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,n){if(!await pathExists(e))return[];let i=JSON.parse(await readFile(e,`utf8`));return(isJsonObject(i)&&isJsonObject(i.dependencies)?i.dependencies[CONNECT_PACKAGE_NAME]:void 0)===n?[]:(await patchPackageJson(e,{dependencies:{[CONNECT_PACKAGE_NAME]:n}}),[{path:e,dependencies:[CONNECT_PACKAGE_NAME],devDependencies:[],scripts:[]}])}function envKeyPresent(e,t){return RegExp(`^\\s*(?:export\\s+)?${t}\\s*=`,`m`).test(e)}async function seedEnvPlaceholders(e,n){if(n.length===0)return[];let r=``;await pathExists(e)&&(r=await readFile(e,`utf8`));let i=n.filter(e=>!envKeyPresent(r,e));if(i.length===0)return[];let a=`${r.length>0&&!r.endsWith(`
`)?`
`:``}${i.map(e=>`${e}=`).join(`
`)}\n`;return await writeFile(e,r+a,`utf8`),i}async function ensureConnection(r){let a=r.slug??r.entry.slug,o=resolveAuth(r.entry),s=endpointForProtocol(r.entry,r.protocol);if(s===null)throw Error(`Connection "${a}" is missing a ${r.protocol===`mcp`?`mcp.url`:`openapi.spec`} endpoint for protocol "${r.protocol}".`);let c=join(r.projectRoot,USER_AUTHORED_CONNECTION_DIR,`${a}.ts`),l=envKeysForAuth(o),u=await pathExists(c);if(!r.force&&u)return{slug:a,protocol:r.protocol,action:`skipped`,filePath:c,filesWritten:[],filesSkipped:[c],packageJsonUpdated:[],envKeysAdded:[],envKeysRequired:l};let d=[];if(o.kind===`connect`){let e=resolveVersionToken(`connectPackageVersion`,r.connectPackageVersion??`0.2.2`);d.push(...await ensureConnectDependency(join(r.projectRoot,`package.json`),e))}await writeTextFile(c,renderTemplate(r.protocol,s,r.entry.description,o),{force:!0});let f=await seedEnvPlaceholders(join(r.projectRoot,`.env.local`),l),p={slug:a,protocol:r.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 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,listAuthoredConnections};