eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 4.7 kB
JavaScript
import{array,boolean,number,object,string}from"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/schemas.js";import"../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js";import{isForbiddenApiFailure}from"./vercel-api-failure.js";import{captureVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";const VERCEL_PROJECT_REQUEST_TIMEOUT_MS=15e3,VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelPaginationSchema=object({next:number().int().nonnegative().nullable().optional()}),VercelTeamPageSchema=object({teams:array(VercelTeamListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.teams,next:e.pagination?.next??void 0})),VercelApiTeamPageSchema=object({teams:array(VercelTeamListEntrySchema.omit({current:!0})),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.teams.map(e=>({...e,current:!1})),next:e.pagination?.next??void 0})),VercelProjectPageSchema=object({projects:array(VercelProjectListEntrySchema),pagination:VercelPaginationSchema.optional()}).transform(e=>({items:e.projects,next:e.pagination?.next??void 0}));function parseVercelJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}async function drainPages(e,t,n){let r=new Map,i=new Set,a;for(;;){let o=await n(a);for(let e of o.items){let n=t(e);r.has(n)||r.set(n,e)}if(o.next===void 0)return[...r.values()];if(i.has(o.next))throw Error(`Vercel returned a repeated pagination cursor for ${e}.`);i.add(o.next),a=o.next}}function requireVercelTeamAccess(e){let t=e.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-forbidden`,command:`vercel login`,reason:`Vercel denied access to this scope.${t?` ${t}`:``} Re-authenticate (for example to complete a team's SSO) or switch to a team you can access.`})}function isUnsupportedTeamListFailure(e){let t=`${e.stderr}\n${e.stdout}`;return/(?:unknown|unexpected|invalid).*(?:--format|--limit)/iu.test(t)}function requireVercelCliUpgrade(e){throw new HumanActionRequiredError({kind:`vercel-cli-upgrade`,command:`vercel upgrade`,reason:`The installed Vercel CLI does not support the team-list options eve needs. ${e.message} Upgrade it and retry.`})}async function captureTeamPage(e,t,n){let r=await captureVercel(n,{cwd:e,signal:t.signal});if(t.signal?.throwIfAborted(),!r.ok)throw isForbiddenApiFailure(r.failure)&&requireVercelTeamAccess(r.failure),isUnsupportedTeamListFailure(r.failure)&&requireVercelCliUpgrade(r.failure),Error(`Could not list Vercel teams. ${r.failure.message}`);return r.stdout}async function fetchTeamPage(e,t,n){if(n===void 0){let n=await captureTeamPage(e,t,[`teams`,`ls`,`--format`,`json`,`--limit`,`100`]),r=VercelTeamPageSchema.safeParse(parseVercelJson(n,`teams`));if(!r.success)throw Error(`Could not read teams from Vercel CLI JSON output.`);return r.data}let r=await captureTeamPage(e,t,[`api`,`/v2/teams?limit=100&until=${n}`]),i=VercelApiTeamPageSchema.safeParse(parseVercelJson(r,`teams`));if(!i.success)throw Error(`Could not read teams from Vercel API JSON output.`);return i.data}async function listTeams(e,t={}){return drainPages(`Vercel teams`,e=>e.slug,n=>fetchTeamPage(e,t,n))}async function fetchProjectPage(e,t,n){let r=[`project`,`ls`,`--format`,`json`,`--scope`,t];n.search!==void 0&&r.push(`--filter`,n.search),n.next!==void 0&&r.push(`--next`,String(n.next));let i=await captureVercel(r,{cwd:e,signal:n.signal,timeoutMs:VERCEL_PROJECT_REQUEST_TIMEOUT_MS});if(n.signal?.throwIfAborted(),!i.ok)throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not list Vercel projects in ${t}. ${i.failure.message}`);let s=VercelProjectPageSchema.safeParse(parseVercelJson(i.stdout,`projects`));if(!s.success)throw Error(`Could not read projects from Vercel CLI JSON output.`);return s.data}async function listRecentProjects(e,t,n={}){return(await fetchProjectPage(e,t,n)).items}function projectSearchRank(e,t){let n=e.name.toLowerCase(),r=t.toLowerCase();return n===r?0:n.startsWith(r)?1:2}function rankProjectSearchResults(e,t){let n=t.trim();return[...e].sort((e,t)=>projectSearchRank(e,n)-projectSearchRank(t,n))}async function searchProjects(e,t,n,r={}){let i=n.trim();if(i.length===0)throw Error(`Project search query cannot be empty.`);let a=await fetchProjectPage(e,t,{...r,search:i}),o=rankProjectSearchResults(a.items,i);return a.next===void 0?{projects:o}:{projects:o,next:a.next}}export{VERCEL_PROJECT_REQUEST_TIMEOUT_MS,listRecentProjects,listTeams,parseVercelJson,rankProjectSearchResults,requireVercelTeamAccess,searchProjects};