eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 6.09 kB
JavaScript
import{select,text}from"../ask.js";import{detectProjectResolution,isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{withSpinner}from"../with-spinner.js";import{pathExists}from"../path-exists.js";import{assertNewProjectNameAvailable,isVercelAuthenticated,pickNewProjectName,pickProject,pickTeam,requireAuth,resolveProjectByNameOrId,resolveTeam,validateTeam}from"../vercel-project.js";import{join,resolve}from"node:path";import{byokProviderEnvVar}from"#setup/scaffold/index.js";import{whimsyFor}from"#setup/cli/index.js";function vercelDemands(e){return{connectSlugs:e.connectionSelection.filter(e=>e.entry.auth?.kind===`connect`).map(e=>e.slug)}}function connectClause(e){let t=e.length===1?`authenticates`:`authenticate`;return`${e.join(`, `)} ${t} through Vercel Connect`}function vercelRequirements(e){let t=[];return e.connectSlugs.length>0&&t.push(connectClause(e.connectSlugs)),t}function resolveProvisioning(e){let t=e.deps??{requireAuth,isVercelAuthenticated,detectProjectResolution,pathExists,validateTeam,resolveTeam,pickTeam,pickProject,pickNewProjectName,assertNewProjectNameAvailable,resolveProjectByNameOrId},parent=()=>resolve(e.targetDirectory??process.cwd());async function detectAdoptableLink(n,r){if(n.projectPath.kind!==`resolved`)return;let i=n.projectPath.path;if(await t.pathExists(join(i,`.vercel`,`project.json`)))return withSpinner(e.prompter,whimsyFor(`project-detect`),async()=>{let e=await t.detectProjectResolution(i,{signal:r});if(isProjectResolved(e)&&await t.isVercelAuthenticated(i,{signal:r}))return e})}async function plansFromFlags(n,r,i,a){if(r.skipVercel)return i.apiKey===void 0?{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:i.apiKey},modelWiring:`gateway`};if(r.team===void 0||r.team.length===0)throw Error(`Headless Vercel provisioning requires --team <slug> or --scope <slug> so the current CLI scope is not applied silently.`);await t.requireAuth(parent(),void 0,{signal:a}),await t.validateTeam(e.prompter,parent(),r.team,{signal:a});let o=await t.resolveTeam(parent(),r.team,{signal:a}),s=i.apiKey===void 0?{kind:`inherit`}:{kind:`byok`,apiGatewayKey:i.apiKey},c;if(r.project===void 0)c={kind:`new`,project:n,team:o};else{let e=await t.resolveProjectByNameOrId(parent(),o,r.project,{signal:a});if(e===null)throw Error(`Vercel project "${r.project}" was not found in ${o}.`);c={kind:`existing`,project:e,team:o}}return c.kind===`new`&&await t.assertNewProjectNameAvailable(parent(),o,c.project,{signal:a}),{vercelProject:c,aiGateway:s,modelWiring:`gateway`}}async function promptPlans(n,r){let i=e.prompter,a=n.agentName,o=e.adoptExistingLink===!1?void 0:await detectAdoptableLink(n,r);if(o!==void 0)return i.log.info(`This directory is already linked to a Vercel project, so your agent will run there — the AI Gateway authenticates through your Vercel login.`),{vercelProject:{kind:`none`},aiGateway:{kind:`inherit`},modelWiring:`gateway`,project:o};let s=vercelRequirements(vercelDemands(n)),c=!0;if(s.length>0?i.log.info(`${s.join(` and `)}, so your agent will run on Vercel.`):c=await e.asker.ask(select({key:`deploy`,message:`Where should your agent run?`,options:[{id:`vercel`,value:!0,label:`On Vercel`,hint:`AI Gateway, Durable Workflow, Sandbox, and more`},{id:`local`,value:!1,label:`Locally for now`,hint:`run with eve dev, deploy any time later`}],recommended:!0,required:!0})),c){await t.requireAuth(parent(),i,{signal:r});let n={signal:r,selectMessage:e.teamSelectMessage},o=await t.pickTeam(i,parent(),void 0,n),s=[{value:`new`,label:`Create a new project`,hint:`Name: ${a}`},{value:`link`,label:`Link an existing project`}],c=e.projectSelection!==`existing-only`&&e.prompter.selectEditable?await e.prompter.selectEditable({message:`Vercel project`,options:s,initialValue:`new`,editable:{value:`new`,defaultValue:a,formatHint:e=>`Name: ${e}`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}}):void 0;if((e.projectSelection===`existing-only`?`link`:c?.value??await e.asker.ask(select({key:`vercel-project`,message:`Vercel project`,options:[{id:`new`,value:`new`,label:`Create a new project`,hint:`Name: ${a}`},{id:`link`,value:`link`,label:`Link an existing project`}],recommended:`new`,required:!0})))===`new`){let e=c?.kind===`edited`?c.text:a;return{vercelProject:{kind:`new`,project:await t.pickNewProjectName(i,parent(),o,e,{signal:r}),team:o},aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return{vercelProject:await t.pickProject(i,parent(),o,{allowCreateWhenEmpty:e.projectSelection!==`existing-only`,suggestedName:a,signal:r}),aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return await e.asker.ask(select({key:`credential`,message:`How should your agent reach the model?`,options:[{id:`api-key`,value:`api-key`,label:`Use my own AI Gateway API key`,hint:`AI_GATEWAY_API_KEY`},{id:`local`,value:`local`,label:`Use my own provider API key`,hint:byokProviderEnvVar(n.modelId)}],recommended:`api-key`,required:!0}))===`api-key`?{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:await e.asker.ask(text({key:`gateway-api-key`,message:`Enter your AI_GATEWAY_API_KEY`,sensitive:!0,environment:`AI_GATEWAY_API_KEY`,validate:e=>e.trim().length===0?`API key cannot be empty.`:null,required:!0}))},modelWiring:`gateway`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}}return{id:`resolve-provisioning`,async gather({state:t,signal:n}){if(e.mode.headless){let r=await plansFromFlags(t.agentName,e.mode.project,e.mode.aiGateway,n);if(r.vercelProject.kind===`none`){let e=vercelDemands(t);if(e.connectSlugs.length>0)throw Error(`${connectClause(e.connectSlugs)}, which needs a Vercel project. Remove --skip-vercel to add ${e.connectSlugs.length===1?`it`:`them`}.`)}return r}return promptPlans(t,n)},async perform({input:e}){return e},apply(e,t){return{...e,vercelProject:t.vercelProject,aiGateway:t.aiGateway,modelWiring:t.modelWiring,project:t.project===void 0?e.project:mergeProjectResolution(e.project,t.project)}}}}export{resolveProvisioning};