eve 0.12.2 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/src/cli/commands/agent-instructions.d.ts +15 -3
- package/dist/src/cli/commands/agent-instructions.js +3 -1
- package/dist/src/cli/commands/agent-prompt/build-and-verify.md +33 -0
- package/dist/src/cli/commands/agent-prompt/collect-intent.md +18 -0
- package/dist/src/cli/commands/agent-prompt/intro-handoff.md +4 -0
- package/dist/src/cli/commands/agent-prompt/intro-setup.md +5 -0
- package/dist/src/cli/commands/agent-prompt/scaffold.md +19 -0
- package/dist/src/cli/commands/agent-prompt/vercel-connect.md +3 -0
- package/dist/src/cli/commands/init-repl.d.ts +79 -0
- package/dist/src/cli/commands/init-repl.js +1 -0
- package/dist/src/cli/commands/init.d.ts +6 -0
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/dev/tui/line-editor.d.ts +2 -0
- package/dist/src/cli/dev/tui/line-editor.js +2 -2
- package/dist/src/cli/dev/tui/prompt-command-handler.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.d.ts +26 -7
- package/dist/src/cli/dev/tui/setup-commands.js +2 -2
- package/dist/src/cli/dev/tui/setup-flow.d.ts +5 -3
- package/dist/src/cli/dev/tui/setup-panel.d.ts +12 -7
- package/dist/src/cli/dev/tui/setup-panel.js +3 -3
- package/dist/src/cli/dev/tui/terminal-renderer.js +10 -10
- package/dist/src/cli/dev/tui/terminal-text.d.ts +11 -0
- package/dist/src/cli/dev/tui/terminal-text.js +1 -1
- package/dist/src/cli/run.d.ts +1 -0
- package/dist/src/cli/run.js +2 -2
- package/dist/src/cli/ui/live-row.js +1 -1
- package/dist/src/cli/ui/progress-pulse.d.ts +10 -0
- package/dist/src/cli/ui/progress-pulse.js +1 -0
- package/dist/src/discover/discover-agent.js +1 -1
- package/dist/src/evals/target.js +1 -1
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel-sdk-types.d.ts +0 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/execution/sandbox/multiplexed-command.d.ts +22 -0
- package/dist/src/execution/sandbox/multiplexed-command.js +1 -0
- package/dist/src/execution/tool-auth.d.ts +13 -3
- package/dist/src/execution/tool-auth.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-module-loader.js +2 -2
- package/dist/src/internal/nitro/dev-runtime-artifacts.js +1 -1
- package/dist/src/public/channels/slack/connections.d.ts +16 -18
- package/dist/src/public/channels/slack/defaults.d.ts +1 -1
- package/dist/src/public/channels/slack/defaults.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +6 -7
- package/dist/src/public/definitions/tool.d.ts +64 -20
- package/dist/src/public/tools/index.d.ts +1 -1
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/connections/validate-authorization.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
- package/dist/src/setup/ai-gateway-api-key.d.ts +15 -0
- package/dist/src/setup/ai-gateway-api-key.js +1 -0
- package/dist/src/setup/boxes/apply-ai-gateway-credential.js +1 -1
- package/dist/src/setup/boxes/detect-ai-gateway.js +2 -2
- package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
- package/dist/src/setup/flows/channels.js +1 -1
- package/dist/src/setup/flows/deploy.js +1 -1
- package/dist/src/setup/flows/install-vercel-cli.js +1 -1
- package/dist/src/setup/flows/link.d.ts +2 -1
- package/dist/src/setup/flows/link.js +1 -1
- package/dist/src/setup/flows/login.js +1 -1
- package/dist/src/setup/flows/model.d.ts +3 -2
- package/dist/src/setup/flows/model.js +1 -1
- package/dist/src/setup/flows/vercel.js +2 -2
- package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/vercel-project.d.ts +2 -6
- package/dist/src/setup/vercel-project.js +1 -1
- package/dist/src/setup/with-spinner.d.ts +3 -0
- package/dist/src/setup/with-spinner.js +1 -0
- package/dist/src/shared/package-name.d.ts +4 -0
- package/dist/src/shared/package-name.js +1 -0
- package/docs/channels/slack.mdx +1 -1
- package/docs/connections.mdx +9 -6
- package/docs/getting-started.mdx +8 -3
- package/docs/guides/auth-and-route-protection.md +59 -10
- package/docs/guides/dev-tui.md +2 -2
- package/docs/reference/cli.md +2 -2
- package/docs/reference/typescript-api.md +9 -7
- package/package.json +1 -1
- package/dist/src/cli/commands/init-agent-handoff.md +0 -12
- package/dist/src/cli/commands/init-agent-instructions.md +0 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection.js";import{ConnectionAuthorizationFailedError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection.js";import{ConnectionAuthorizationFailedError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{stampChallengeDisplayName}from"#runtime/connections/scoped-authorization.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";const logger=createLogger(`framework.connection-search-dynamic`),CONNECTION_SEARCH_OUTPUT_SCHEMA={items:{additionalProperties:!1,properties:{connection:{type:`string`},description:{type:`string`},error:{type:`string`},inputSchema:{type:`object`},needsAuthorization:{type:`boolean`},outputSchema:{type:`object`},qualifiedName:{type:`string`},tool:{type:`string`}},required:[`connection`,`description`],type:`object`},type:`array`},ConnectionSearchResultsKey=new ContextKey(`eve.connectionSearchResults`);function qualifiedConnectionToolName(e,t){return`${e}__${t}`}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n?.authorization&&supportsInteractiveAuthorization(n.authorization))return n.authorization}async function completePendingAuthorizations(e){let n=loadContext(),r=new Set;for(let t of e.getConnections()){let i=getAuthorizationResult(t.connectionName);if(!i)continue;let a=resolveInteractiveAuth(e,t.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(t.connectionName,a),c=await a.completeAuthorization({callbackUrl:i.hookUrl,connection:{url:t.url??``},principal:o,resume:i.resume,callback:i.callback});writeCachedToken(n,t.connectionName,principalKey(o),c),r.add(t.connectionName)}return r}async function executeConnectionSearch(e){let n=loadContext(),i=n.get(ConnectionRegistryKey);if(i===void 0)return[];let s=await completePendingAuthorizations(i),l=e.limit??10,u=tokenize(e.keywords),d=[],p=[],h=e.connection!==void 0&&e.connection!==``?i.getConnections().filter(t=>t.connectionName===e.connection):i.getConnections(),g=[];for(let e of h){let t;try{t=await i.getClient(e.connectionName).getToolMetadata()}catch(t){if(isConnectionAuthorizationRequiredError(t)){if(s.has(e.connectionName)){logger.warn(`connection still unauthorized after authorization`,{connection:e.connectionName}),p.push({connection:e.connectionName,description:e.description,error:`Authorization for "${e.connectionName}" did not take effect; the token was rejected after sign-in.`});continue}let t=resolveInteractiveAuth(i,e.connectionName);if(t){let n=getHookUrl(e.connectionName);if(n){let r=resolveConnectionPrincipal(e.connectionName,t);try{let{challenge:i,resume:a}=await t.startAuthorization({callbackUrl:n,connection:{url:e.url??``},principal:r});g.push({name:e.connectionName,challenge:stampChallengeDisplayName(i,t),hookUrl:n,resume:a})}catch(t){logger.warn(`startAuthorization failed`,{connection:e.connectionName,error:t instanceof Error?t:Error(String(t))})}}}p.push({connection:e.connectionName,description:e.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(t)){logger.warn(`connection authorization failed`,{connection:e.connectionName,reason:t.reason,retryable:t.retryable,error:t}),p.push({connection:e.connectionName,description:e.description,error:`Authorization failed for ${e.connectionName}: ${t.message}`});continue}let n=t instanceof Error?t.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:e.connectionName,error:t instanceof Error?t:Error(n)}),p.push({connection:e.connectionName,description:e.description,error:`Failed to load tools for "${e.connectionName}": ${n}`});continue}for(let n of t){let t=scoreMatch(u,n);t>0&&d.push({item:{connection:e.connectionName,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,qualifiedName:qualifiedConnectionToolName(e.connectionName,n.name),tool:n.name},score:t})}}if(g.length>0)return requestAuthorization(g);d.sort((e,t)=>t.score-e.score);let _=d.slice(0,l).map(e=>e.item);if(_.length>0){let e=[..._,...p],t=n.get(ConnectionSearchResultsKey)??[],r=new Map(t.map(e=>[e.qualifiedName,e]));for(let e of _)e.qualifiedName&&r.set(e.qualifiedName,e);return n.set(ConnectionSearchResultsKey,[...r.values()]),e}return h.map(e=>p.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}function extractDiscoveredTools(e){let t=new Map;for(let n of e){if(n.role!==`tool`)continue;let e=n.content;for(let n of e){if(n.type!==`tool-result`||n.toolName!==`connection_search`)continue;let e=n.output;if(e==null)continue;let r=typeof e==`object`&&`type`in e&&`value`in e?e.value:e;if(Array.isArray(r))for(let e of r)e.tool&&e.qualifiedName&&t.set(e.qualifiedName,e)}}return[...t.values()]}function createConnectionSearchEvents(){return{"step.started":async(e,n)=>{let a=loadContext().get(ConnectionRegistryKey);if(!a||a.getConnections().length===0)return null;let f=a.getConnections().map(e=>e.connectionName),h=extractDiscoveredTools(n.messages),g=loadContext().get(ConnectionSearchResultsKey)??[],_=new Map;for(let e of g)e.qualifiedName&&_.set(e.qualifiedName,e);for(let e of h)e.qualifiedName&&_.set(e.qualifiedName,e);let v=[..._.values()],y={};y.connection_search={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. \`linear__list_issues\`) in your next response. Available connections: ${f.join(`, `)}.`,inputSchema:{type:`object`,additionalProperties:!1,properties:{keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`},connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`}},required:[`keywords`]},async execute(e){return executeConnectionSearch(e)},outputSchema:CONNECTION_SEARCH_OUTPUT_SCHEMA};for(let e of v){let n=e.connection,f=e.tool,p=a.getConnectionApproval(n);y[qualifiedConnectionToolName(n,f)]={description:e.description,inputSchema:e.inputSchema??{type:`object`},needsApproval:p,outputSchema:e.outputSchema,async execute(e){let a=loadContext().get(ConnectionRegistryKey),p=a.getConnections().find(e=>e.connectionName===n),m=p?.authorization&&supportsInteractiveAuthorization(p.authorization)?p.authorization:void 0,h=!1;if(m){let e=getAuthorizationResult(n);if(e){h=!0;let r=loadContext(),i=resolveConnectionPrincipal(n,m),a=await m.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:p?.url??``},principal:i,resume:e.resume,callback:e.callback});writeCachedToken(r,n,principalKey(i),a)}}try{return await a.getClient(n).executeTool(f,e)}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!m)throw e;if(h)throw new ConnectionAuthorizationFailedError(n,{retryable:!1,reason:`token_rejected_after_authorization`,message:`Connection "${n}" rejected the token immediately after authorization.`});let t=getHookUrl(n);if(!t)throw e;let r=resolveConnectionPrincipal(n,m),{challenge:a,resume:s}=await m.startAuthorization({callbackUrl:t,connection:{url:p?.url??``},principal:r});return requestAuthorization([{name:n,challenge:stampChallengeDisplayName(a,m),hookUrl:t,resume:s}])}}}}return y}}}function createConnectionSearchResolver(){let e=createConnectionSearchEvents();return{slug:`connection`,eventNames:Object.keys(e),events:e,sourceId:`eve:connection-search-dynamic`,sourceKind:`module`,logicalPath:`eve:framework/connection-search-dynamic`}}export{createConnectionSearchEvents,createConnectionSearchResolver,extractDiscoveredTools};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { appendEnv } from "./append-env.js";
|
|
2
|
+
export declare const AI_GATEWAY_API_KEY_ENV_VAR = "AI_GATEWAY_API_KEY";
|
|
3
|
+
export declare const AI_GATEWAY_API_KEY_ENV_FILE = ".env.local";
|
|
4
|
+
/** The location written by eve when a user supplies an AI Gateway API key. */
|
|
5
|
+
export interface AiGatewayApiKeyLocation {
|
|
6
|
+
envKey: typeof AI_GATEWAY_API_KEY_ENV_VAR;
|
|
7
|
+
envFile: typeof AI_GATEWAY_API_KEY_ENV_FILE;
|
|
8
|
+
envPath: string;
|
|
9
|
+
}
|
|
10
|
+
/** Trims and saves an explicit AI Gateway API key, replacing any prior value. */
|
|
11
|
+
export declare function writeAiGatewayApiKey(input: {
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
apiKey: string;
|
|
14
|
+
appendEnv?: typeof appendEnv;
|
|
15
|
+
}): Promise<AiGatewayApiKeyLocation>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{appendEnv}from"./append-env.js";import{join}from"node:path";const AI_GATEWAY_API_KEY_ENV_VAR=`AI_GATEWAY_API_KEY`,AI_GATEWAY_API_KEY_ENV_FILE=`.env.local`;async function writeAiGatewayApiKey(e){let t=join(e.projectRoot,AI_GATEWAY_API_KEY_ENV_FILE);return await(e.appendEnv??appendEnv)(t,{[AI_GATEWAY_API_KEY_ENV_VAR]:e.apiKey.trim()},{force:!0}),{envKey:AI_GATEWAY_API_KEY_ENV_VAR,envFile:AI_GATEWAY_API_KEY_ENV_FILE,envPath:t}}export{AI_GATEWAY_API_KEY_ENV_FILE,AI_GATEWAY_API_KEY_ENV_VAR,writeAiGatewayApiKey};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{appendEnv}from"../append-env.js";import{isProjectResolved}from"../project-resolution.js";import{requireProjectPath}from"../state.js";import{
|
|
1
|
+
import{appendEnv}from"../append-env.js";import{AI_GATEWAY_API_KEY_ENV_FILE,AI_GATEWAY_API_KEY_ENV_VAR,writeAiGatewayApiKey}from"../ai-gateway-api-key.js";import{isProjectResolved}from"../project-resolution.js";import{requireProjectPath}from"../state.js";import{withSpinner}from"../with-spinner.js";import{runVercelEnvPull}from"../run-vercel-link.js";import{detectAiGatewayResolution}from"./detect-ai-gateway.js";import{createPromptCommandOutput}from"#setup/cli/index.js";function applyAiGatewayCredential(a){let o=a.deps??{appendEnv,runVercelEnvPull,detectAiGatewayResolution};return{id:`apply-ai-gateway-credential`,shouldRun(e){let{aiGateway:t,project:n}=e;return t.kind===`byok`||t.kind===`inherit`&&isProjectResolved(n)},async gather(){return null},async perform({state:e,signal:r}){let{prompter:i}=a,s=requireProjectPath(e),c=e.aiGateway,l=isProjectResolved(e.project);if(c.kind===`byop`)return{kind:`unresolved`};if(c.kind===`byok`){let e=await writeAiGatewayApiKey({projectRoot:s,apiKey:c.apiGatewayKey,appendEnv:o.appendEnv});return a.prompter.log.success(`Wrote ${e.envKey} to ${e.envPath}`),{kind:`api-key`,envFile:e.envFile}}if(!l)return i.log.warning(`No Vercel project linked and no API key provided. The agent will not reach a model until you set ${AI_GATEWAY_API_KEY_ENV_VAR} in ${AI_GATEWAY_API_KEY_ENV_FILE} or link a project.`),{kind:`unresolved`};let u=createPromptCommandOutput(i.log),d=await withSpinner(i,`Pulling Vercel environment variables into .env.local...`,()=>o.runVercelEnvPull(s,u,r));return r?.throwIfAborted(),d||i.log.warning(`Linked the project, but pulling environment variables did not complete.`),o.detectAiGatewayResolution(s)},apply(e,t){return{...e,aiGatewayCredentials:t}}}}export{applyAiGatewayCredential};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{requireProjectPath}from"../state.js";import{join}from"node:path";import{readFile}from"node:fs/promises";const ENV_FILE_CANDIDATES=[
|
|
2
|
-
`))if(readEnvValue(e,t)!==void 0)return!0;return!1}async function findEnvFileWithKey(e,
|
|
1
|
+
import{AI_GATEWAY_API_KEY_ENV_FILE,AI_GATEWAY_API_KEY_ENV_VAR}from"../ai-gateway-api-key.js";import{requireProjectPath}from"../state.js";import{join}from"node:path";import{readFile}from"node:fs/promises";const ENV_FILE_CANDIDATES=[AI_GATEWAY_API_KEY_ENV_FILE,`.env`];function readEnvValue(e,t){let n=e.trim();if(n.length===0||n.startsWith(`#`)||!n.startsWith(`${t}=`))return;let r=n.slice(t.length+1);if(r.length===0)return;let i=r.length>=2&&r.startsWith(`"`)&&r.endsWith(`"`)?r.slice(1,-1):r;return i.length===0?void 0:i}async function hasApiKeyInFile(e,t){let n;try{n=await readFile(e,`utf8`)}catch{return!1}for(let e of n.split(`
|
|
2
|
+
`))if(readEnvValue(e,t)!==void 0)return!0;return!1}async function findEnvFileWithKey(e,t){for(let n of ENV_FILE_CANDIDATES)if(await hasApiKeyInFile(join(e,n),t))return n}async function detectAiGatewayResolution(e){let n=await findEnvFileWithKey(e,AI_GATEWAY_API_KEY_ENV_VAR);return n===void 0?{kind:`unresolved`}:{kind:`api-key`,envFile:n}}function detectAiGateway(){return{id:`detect-ai-gateway`,shouldRun(e){return e.projectPath.kind===`resolved`},async gather(){return null},async perform({state:e}){return detectAiGatewayResolution(requireProjectPath(e))},apply(e,t){return{...e,aiGatewayCredentials:t}}}}export{detectAiGateway,detectAiGatewayResolution,findEnvFileWithKey};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{select,text}from"../ask.js";import{detectProjectResolution,isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{assertNewProjectNameAvailable,isVercelAuthenticated,pickNewProjectName,pickProject,pickTeam,requireAuth,resolveTeam,validateTeam
|
|
1
|
+
import{select,text}from"../ask.js";import{detectProjectResolution,isProjectResolved,mergeProjectResolution}from"../project-resolution.js";import{withSpinner}from"../with-spinner.js";import{assertNewProjectNameAvailable,isVercelAuthenticated,pickNewProjectName,pickProject,pickTeam,requireAuth,resolveTeam,validateTeam}from"../vercel-project.js";import{pathExists}from"../path-exists.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{slack:e.channelSelection.includes(`slack`),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.slack&&t.push(`Slack needs a public URL`),e.connectSlugs.length>0&&t.push(connectClause(e.connectSlugs)),t}function resolveProvisioning(l){let u=l.deps??{requireAuth,isVercelAuthenticated,detectProjectResolution,pathExists,validateTeam,resolveTeam,pickTeam,pickProject,pickNewProjectName,assertNewProjectNameAvailable},parent=()=>resolve(l.targetDirectory??process.cwd());async function detectAdoptableLink(e,t){if(e.projectPath.kind!==`resolved`)return;let n=e.projectPath.path;if(await u.pathExists(join(n,`.vercel`,`project.json`)))return withSpinner(l.prompter,whimsyFor(`project-detect`),async()=>{let e=await u.detectProjectResolution(n,{signal:t});if(!(!isProjectResolved(e)||!await u.isVercelAuthenticated(n,{signal:t})))return e})}async function plansFromFlags(e,t,n,r){if(t.skipVercel)return n.apiKey===void 0?{vercelProject:{kind:`none`},aiGateway:{kind:`byop`},modelWiring:`self`}:{vercelProject:{kind:`none`},aiGateway:{kind:`byok`,apiGatewayKey:n.apiKey},modelWiring:`gateway`};if(t.team===void 0||t.team.length===0)throw Error(`Headless Vercel provisioning requires --team <slug> or --scope <slug> so the current CLI scope is not applied silently.`);await u.requireAuth(parent(),void 0,{signal:r}),await u.validateTeam(l.prompter,parent(),t.team,{signal:r});let i=await u.resolveTeam(parent(),t.team,{signal:r}),a=n.apiKey===void 0?{kind:`inherit`}:{kind:`byok`,apiGatewayKey:n.apiKey},o=t.project===void 0?{kind:`new`,project:e,team:i}:{kind:`existing`,project:t.project,team:i};return o.kind===`new`&&await u.assertNewProjectNameAvailable(parent(),i,o.project,{signal:r}),{vercelProject:o,aiGateway:a,modelWiring:`gateway`}}async function promptPlans(n,r){let i=l.prompter,a=n.agentName,o=l.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 l.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 u.requireAuth(parent(),i,{signal:r});let t=await u.pickTeam(i,parent(),void 0,{signal:r}),n=[{value:`new`,label:`Create a new project`,hint:`Named ${a}`},{value:`link`,label:`Link an existing project`}],o=l.projectSelection!==`existing-only`&&l.prompter.selectEditable?await l.prompter.selectEditable({message:`Vercel project`,options:n,initialValue:`new`,editable:{value:`new`,defaultValue:a,formatHint:e=>`Named ${e}`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}}):void 0;if((l.projectSelection===`existing-only`?`link`:o?.value??await l.asker.ask(select({key:`vercel-project`,message:`Vercel project`,options:[{id:`new`,value:`new`,label:`Create a new project`,hint:`Named ${a}`},{id:`link`,value:`link`,label:`Link an existing project`}],recommended:`new`,required:!0})))===`new`){let e=o?.kind===`edited`?o.text:a;return{vercelProject:{kind:`new`,project:await u.pickNewProjectName(i,parent(),t,e,{signal:r}),team:t},aiGateway:{kind:`inherit`},modelWiring:`gateway`}}let s=await u.pickProject(i,parent(),t,{allowCreateWhenEmpty:l.projectSelection!==`existing-only`,signal:r});return{vercelProject:{kind:s.exists?`existing`:`new`,project:s.project,team:t},aiGateway:{kind:`inherit`},modelWiring:`gateway`}}return await l.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 l.asker.ask(text({key:`gateway-api-key`,message:`Enter your AI_GATEWAY_API_KEY`,sensitive:!0,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:e,signal:t}){if(l.mode.headless){let n=await plansFromFlags(e.agentName,l.mode.project,l.mode.aiGateway,t);if(n.vercelProject.kind===`none`){let t=vercelDemands(e);if(t.slack)throw Error(`Slack requires a Vercel project. Remove --skip-vercel to add Slack.`);if(t.connectSlugs.length>0)throw Error(`${connectClause(t.connectSlugs)}, which needs a Vercel project. Remove --skip-vercel to add ${t.connectSlugs.length===1?`it`:`them`}.`)}return n}return promptPlans(e,t)},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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{interactiveAsker}from"../ask.js";import{detectDeployment,isProjectResolved,projectResolutionFromDeployment}from"../project-resolution.js";import{createDefaultSetupState,snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{addChannels}from"../boxes/add-channels.js";import{getVercelAuthStatus}from"../vercel-project.js";import{CHANNELS_PROMPT_MESSAGE,selectChannels}from"../boxes/select-channels.js";import{assertCanAddSelectedChannels,inspectExistingChannelRegistrations}from"../channel-add-conflicts.js";import{runInteractive}from"../runner.js";import{prompterSink}from"./in-project.js";import{SCAFFOLDABLE_CHANNELS}from"#setup/scaffold/index.js";import{toErrorMessage}from"#shared/errors.js";const SEE_IT_LIVE_MESSAGE=`See it live`;async function promptSeeItLive(e){try{return await e.select({message:SEE_IT_LIVE_MESSAGE,options:[{value:`deploy`,label:`Deploy and chat`},{value:`later`,label:`Later`}]})}catch(e){if(e instanceof WizardCancelledError)return`later`;throw e}}function channelAlreadyAdded(e,t){return t===`web`?e.webAppPresent:e.slackOwners.length>0}function appendChannel(e,t){return e.includes(t)?[...e]:[...e,t]}async function pickChannel(e,t,n,r){let i=channelListRows(t,n,r),a=!i.some(e=>e.value!==`done`&&e.completed!==!0&&e.disabled!==!0),s={message:CHANNELS_PROMPT_MESSAGE,options:i,hintLayout:`inline`};a&&(s.initialValue=`done`);try{return{kind:`picked`,value:await e.select(s)}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}function channelLandedDuringSubflow(e,t,n){return!channelAlreadyAdded(e,n)&&channelAlreadyAdded(t,n)}function deployAndChatDetails(e){let t={};return e.slackChatUrl!==void 0&&(t.chatUrl=e.slackChatUrl),e.slackWorkspaceName!==void 0&&(t.workspaceName=e.slackWorkspaceName),t}function vercelChannelBlocker(e,t){
|
|
1
|
+
import{interactiveAsker}from"../ask.js";import{detectDeployment,isProjectResolved,projectResolutionFromDeployment}from"../project-resolution.js";import{createDefaultSetupState,snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{addChannels}from"../boxes/add-channels.js";import{withSpinner}from"../with-spinner.js";import{getVercelAuthStatus,vercelAuthBlockerReason}from"../vercel-project.js";import{CHANNELS_PROMPT_MESSAGE,selectChannels}from"../boxes/select-channels.js";import{assertCanAddSelectedChannels,inspectExistingChannelRegistrations}from"../channel-add-conflicts.js";import{runInteractive}from"../runner.js";import{prompterSink}from"./in-project.js";import{SCAFFOLDABLE_CHANNELS}from"#setup/scaffold/index.js";import{toErrorMessage}from"#shared/errors.js";const SEE_IT_LIVE_MESSAGE=`See it live`;async function promptSeeItLive(e){try{return await e.select({message:SEE_IT_LIVE_MESSAGE,options:[{value:`deploy`,label:`Deploy and chat`},{value:`later`,label:`Later`}]})}catch(e){if(e instanceof WizardCancelledError)return`later`;throw e}}function channelAlreadyAdded(e,t){return t===`web`?e.webAppPresent:e.slackOwners.length>0}function appendChannel(e,t){return e.includes(t)?[...e]:[...e,t]}async function pickChannel(e,t,n,r){let i=channelListRows(t,n,r),a=!i.some(e=>e.value!==`done`&&e.completed!==!0&&e.disabled!==!0),s={message:CHANNELS_PROMPT_MESSAGE,options:i,hintLayout:`inline`};a&&(s.initialValue=`done`);try{return{kind:`picked`,value:await e.select(s)}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}function channelLandedDuringSubflow(e,t,n){return!channelAlreadyAdded(e,n)&&channelAlreadyAdded(t,n)}function deployAndChatDetails(e){let t={};return e.slackChatUrl!==void 0&&(t.chatUrl=e.slackChatUrl),e.slackWorkspaceName!==void 0&&(t.workspaceName=e.slackWorkspaceName),t}function vercelChannelBlocker(e,t){let n=vercelAuthBlockerReason(e);if(n!==void 0)return n;if(!t)return`Requires Vercel account, see /model`}function channelListRows(e,t,n){let r=[{value:`repl`,label:`Terminal UI`,completed:!0,focusHint:`Already installed`}];for(let i of SCAFFOLDABLE_CHANNELS){if(channelAlreadyAdded(e,i.kind)){r.push({value:i.kind,label:i.label,completed:!0,focusHint:`Already installed`});continue}let a=e.disabledChannelReasons[i.kind];if(a!==void 0){r.push({value:i.kind,label:i.label,disabled:!0,disabledReason:a});continue}if(i.requiresVercelProject===!0){let e=vercelChannelBlocker(n,t);if(e!==void 0){r.push({value:i.kind,label:i.label,disabled:!0,disabledReason:e,disabledReasonTone:`warning`});continue}}let o={value:i.kind,label:i.label};i.hint!==void 0&&(o.hint=i.hint),r.push(o)}return r.push({value:`done`,label:`Done`}),r}async function runChannelsFlow(o){let{appRoot:c,prompter:l,signal:u}=o,d={detectDeployment,inspectExistingChannelRegistrations,getVercelAuthStatus,...o.deps},[f,p,m]=await withSpinner(l,`Checking the project…`,()=>Promise.all([d.detectDeployment(c,{signal:u}),d.inspectExistingChannelRegistrations(c),d.getVercelAuthStatus(c,{signal:u})]));u?.throwIfAborted();let h=p,g={...createDefaultSetupState(),project:projectResolutionFromDeployment(f),projectPath:{kind:`resolved`,inPlace:!0,path:c}},_;for(;;){let t=await pickChannel(l,h,isProjectResolved(g.project),m);if(t.kind===`cancelled`){if(g.channels.length===0)return{kind:`cancelled`};break}let r=t.value;if(r===`done`)break;if(r===`repl`||channelAlreadyAdded(h,r))continue;let i=[selectChannels({asker:interactiveAsker(l),variant:`channels-add`,presetChannels:[r],validateSelection:e=>assertCanAddSelectedChannels(e,h)}),addChannels({asker:interactiveAsker(l),prompter:l,configureVercelServices:!0,deps:d.addChannels})],o;try{o=await runInteractive(i,g,prompterSink(l),{snapshot:snapshotSetupState,signal:u})}catch(e){let t=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c));if(channelLandedDuringSubflow(h,t,r)){if(g={...g,channels:appendChannel(g.channels,r)},h=t,_=toErrorMessage(e),u?.aborted===!0)break;continue}throw e}if(o.kind===`done`){if(g=o.state,h=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c)),u?.throwIfAborted(),r===`slack`&&g.slackbotAttached&&await promptSeeItLive(l)===`deploy`)return{kind:`deploy-and-chat`,addedChannels:g.channels,chat:deployAndChatDetails(g)}}else{let e=await withSpinner(l,`Checking the project…`,()=>d.inspectExistingChannelRegistrations(c));if(channelLandedDuringSubflow(h,e,r))return{kind:`done`,addedChannels:appendChannel(g.channels,r)};h=e}}return _===void 0?{kind:`done`,addedChannels:g.channels}:{kind:`failed`,addedChannels:g.channels,message:_}}export{SEE_IT_LIVE_MESSAGE,runChannelsFlow};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{interactiveAsker,withAnswers}from"../ask.js";import{detectDeployment,isProjectResolved,projectResolutionFromDeployment}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{deployProject}from"../boxes/deploy-project.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{runHeadless,runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";function productionUrlOf(e){return e.kind===`deployed`?e.productionUrl:void 0}async function runDeployFlow(t){let{appRoot:n,prompter:r,interactive:i,signal:a}=t,o={detectDeployment,...t.deps},s=r
|
|
1
|
+
import{interactiveAsker,withAnswers}from"../ask.js";import{detectDeployment,isProjectResolved,projectResolutionFromDeployment}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{withSpinner}from"../with-spinner.js";import{deployProject}from"../boxes/deploy-project.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{runHeadless,runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";function productionUrlOf(e){return e.kind===`deployed`?e.productionUrl:void 0}async function runDeployFlow(t){let{appRoot:n,prompter:r,interactive:i,signal:a}=t,o={detectDeployment,...t.deps},s=await withSpinner(r,`Checking the current Vercel link...`,async()=>{let e=await o.detectDeployment(n,{signal:a});return a?.throwIfAborted(),projectResolutionFromDeployment(e)}),c=isProjectResolved(s);if(!c&&!i)return{kind:`needs-link`};let l=inProjectSetupState(n,s,{deploymentPending:!0}),u=c?[deployProject({prompter:r,headless:!i,deps:o.deployProject})]:[resolveProvisioning({asker:withAnswers({deploy:`vercel`})(interactiveAsker(r)),prompter:r,targetDirectory:n,mode:{headless:!1},deps:o.resolveProvisioning}),linkVercelProject({prompter:r,deps:o.linkProject}),deployProject({prompter:r,headless:!i,deps:o.deployProject})],d=prompterSink(r);if(!i)return{kind:`deployed`,productionUrl:productionUrlOf((await runHeadless(u,l,d,{snapshot:snapshotSetupState,signal:a})).project)};let f=await runInteractive(u,l,d,{snapshot:snapshotSetupState,signal:a});return f.kind===`cancelled`?{kind:`cancelled`}:{kind:`deployed`,productionUrl:productionUrlOf(f.state.project)}}export{runDeployFlow};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{detectPackageManager}from"#setup/package-manager.js";import{spawnPackageManager}from"#setup/primitives/index.js";import{getVercelAuthStatus}from"#setup/vercel-project.js";import{createPromptCommandOutput}from"#setup/cli/index.js";const defaultDeps={getVercelAuthStatus,detectPackageManager,spawnPackageManager};function globalInstallArguments(e){switch(e){case`npm`:return[`install`,`-g`,`vercel@latest`];case`yarn`:return[`global`,`add`,`vercel@latest`];case`pnpm`:case`bun`:return[`add`,`-g`,`vercel@latest`]}}async function
|
|
1
|
+
import{withSpinner}from"../with-spinner.js";import{detectPackageManager}from"#setup/package-manager.js";import{spawnPackageManager}from"#setup/primitives/index.js";import{getVercelAuthStatus}from"#setup/vercel-project.js";import{createPromptCommandOutput}from"#setup/cli/index.js";const defaultDeps={getVercelAuthStatus,detectPackageManager,spawnPackageManager};function globalInstallArguments(e){switch(e){case`npm`:return[`install`,`-g`,`vercel@latest`];case`yarn`:return[`global`,`add`,`vercel@latest`];case`pnpm`:case`bun`:return[`add`,`-g`,`vercel@latest`]}}async function runInstallVercelCliFlow(t){let{appRoot:n,prompter:r,signal:i}=t,a={...defaultDeps,...t.deps},o=createPromptCommandOutput(r.log),probe=async()=>await a.getVercelAuthStatus(n,{signal:i})!==`cli-missing`;if(await withSpinner(r,`Checking for the Vercel CLI…`,probe))return i?.throwIfAborted(),{kind:`already`};i?.throwIfAborted();let s=await a.detectPackageManager(n),c=await withSpinner(r,`Installing the Vercel CLI with ${s.kind}…`,()=>a.spawnPackageManager(s.kind,n,globalInstallArguments(s.kind),{onOutput:o,signal:i,nonInteractive:!0}));if(i?.aborted===!0)return{kind:`cancelled`};if(!c)return{kind:`failed`};let l=await withSpinner(r,`Verifying the Vercel CLI…`,probe);return i?.throwIfAborted(),l?{kind:`installed`}:{kind:`failed`}}export{runInstallVercelCliFlow};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AI_GATEWAY_API_KEY_ENV_VAR } from "../ai-gateway-api-key.js";
|
|
1
2
|
import { type ApplyAiGatewayCredentialDeps } from "../boxes/apply-ai-gateway-credential.js";
|
|
2
3
|
import { findEnvFileWithKey } from "../boxes/detect-ai-gateway.js";
|
|
3
4
|
import { type LinkProjectDeps } from "../boxes/link-project.js";
|
|
@@ -15,7 +16,7 @@ export interface LinkFlowDeps {
|
|
|
15
16
|
export type LinkFlowResult = {
|
|
16
17
|
kind: "done";
|
|
17
18
|
/** The model credential verified in an env file, when one landed. */
|
|
18
|
-
credential?: "VERCEL_OIDC_TOKEN" |
|
|
19
|
+
credential?: "VERCEL_OIDC_TOKEN" | typeof AI_GATEWAY_API_KEY_ENV_VAR;
|
|
19
20
|
} | {
|
|
20
21
|
kind: "cancelled";
|
|
21
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{interactiveAsker,withAnswers}from"../ask.js";import{detectProjectIdentity}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{detectAiGateway,findEnvFileWithKey}from"../boxes/detect-ai-gateway.js";import{applyAiGatewayCredential}from"../boxes/apply-ai-gateway-credential.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";var import_picocolors=__toESM(require_picocolors(),1);async function runLinkFlow(e){let{appRoot:t,prompter:i,signal:a,projectSelection:o=`existing-only`}=e,s={detectProjectIdentity,findEnvFileWithKey,...e.deps},c=i
|
|
1
|
+
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{AI_GATEWAY_API_KEY_ENV_VAR}from"../ai-gateway-api-key.js";import{interactiveAsker,withAnswers}from"../ask.js";import{detectProjectIdentity}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{withSpinner}from"../with-spinner.js";import{detectAiGateway,findEnvFileWithKey}from"../boxes/detect-ai-gateway.js";import{applyAiGatewayCredential}from"../boxes/apply-ai-gateway-credential.js";import{linkVercelProject}from"../boxes/link-project.js";import{resolveProvisioning}from"../boxes/resolve-provisioning.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";var import_picocolors=__toESM(require_picocolors(),1);async function runLinkFlow(e){let{appRoot:t,prompter:i,signal:a,projectSelection:o=`existing-only`}=e,s={detectProjectIdentity,findEnvFileWithKey,...e.deps},c=await withSpinner(i,`Checking the current Vercel link...`,async()=>{let e=await s.detectProjectIdentity(t,{signal:a});return a?.throwIfAborted(),e});if(c===void 0){let[e,n]=await Promise.all([s.findEnvFileWithKey(t,AI_GATEWAY_API_KEY_ENV_VAR),s.findEnvFileWithKey(t,`VERCEL_OIDC_TOKEN`)]),r=e??n;r!==void 0&&i.log.message(`This directory is not linked to a Vercel project yet — the model currently runs on credentials from ${r}.`)}else{let e=c.teamName===void 0?import_picocolors.default.bold(c.projectName):`${import_picocolors.default.bold(c.projectName)} in ${import_picocolors.default.bold(c.teamName)}`;try{if(await i.select({message:`This directory is already linked to\n${e}`,options:[{value:`relink`,label:`Link to another project`},{value:`dismiss`,label:`Dismiss`}]})===`dismiss`)return{kind:`cancelled`}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}}let l=inProjectSetupState(t,{kind:`unresolved`});if((await runInteractive([resolveProvisioning({asker:withAnswers({deploy:`vercel`})(interactiveAsker(i)),prompter:i,targetDirectory:t,mode:{headless:!1},adoptExistingLink:!1,projectSelection:o,deps:s.resolveProvisioning}),linkVercelProject({prompter:i,deps:s.linkProject}),detectAiGateway(),applyAiGatewayCredential({prompter:i,deps:s.applyAiGatewayCredential})],l,prompterSink(i),{snapshot:snapshotSetupState,signal:a})).kind===`cancelled`)return{kind:`cancelled`};let[u,d]=await Promise.all([s.findEnvFileWithKey(t,`VERCEL_OIDC_TOKEN`),s.findEnvFileWithKey(t,AI_GATEWAY_API_KEY_ENV_VAR)]);a?.throwIfAborted(),u===void 0&&d===void 0&&i.log.warning("Linked, but no model credential landed in an env file (VERCEL_OIDC_TOKEN or AI_GATEWAY_API_KEY). Run `vercel env pull` once the project has AI Gateway access.");let f={kind:`done`};return u===void 0?d!==void 0&&(f.credential=AI_GATEWAY_API_KEY_ENV_VAR):f.credential=`VERCEL_OIDC_TOKEN`,f}export{runLinkFlow};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getVercelAuthStatus}from"#setup/vercel-project.js";import{createPromptCommandOutput}from"#setup/cli/index.js";import{runVercel}from"#setup/primitives/run-vercel.js";const defaultDeps={getVercelAuthStatus,runVercelLogin:({cwd:e,onOutput:t,signal:
|
|
1
|
+
import{withSpinner}from"../with-spinner.js";import{getVercelAuthStatus}from"#setup/vercel-project.js";import{createPromptCommandOutput}from"#setup/cli/index.js";import{runVercel}from"#setup/primitives/run-vercel.js";const defaultDeps={getVercelAuthStatus,runVercelLogin:({cwd:e,onOutput:t,signal:n})=>runVercel([`login`],{cwd:e,nonInteractive:!0,onOutput:t,timeoutMs:3e5,signal:n})};async function runVercelLoginWithControls(t,n,r,i,a){if(i.awaitChoice===void 0)return withSpinner(i,`Opening Vercel login in your browser…`,()=>t.runVercelLogin({cwd:n,onOutput:r,signal:a}));let o=new AbortController,s=a?AbortSignal.any([a,o.signal]):o.signal,c=i.awaitChoice({status:`Logging in to Vercel…`,context:`Complete the login in your browser`,actions:[{value:`cancel`,label:`Cancel`}]}),l=t.runVercelLogin({cwd:n,onOutput:r,signal:s});try{let e=await Promise.race([l.then(e=>({via:`work`,ok:e})),c.choice.then(()=>({via:`choice`}))]);return e.via===`work`?e.ok:(o.abort(),await l.catch(()=>{}),`cancelled`)}finally{c.close()}}async function runLoginFlow(t){let{appRoot:r,prompter:a,signal:o}=t,s={...defaultDeps,...t.deps},c=createPromptCommandOutput(a.log),probeAuth=()=>s.getVercelAuthStatus(r,{signal:o}),l=await withSpinner(a,`Checking your Vercel login…`,probeAuth);switch(o?.throwIfAborted(),l){case`authenticated`:return{kind:`already`};case`cli-missing`:return{kind:`cli-missing`};case`unavailable`:return{kind:`unavailable`};case`logged-out`:break;default:return l}let u=await runVercelLoginWithControls(s,r,c,a,o);if(u===`cancelled`)return{kind:`cancelled`};if(!u)return{kind:`failed`};let d=await withSpinner(a,`Confirming your Vercel login…`,probeAuth);switch(o?.throwIfAborted(),d){case`authenticated`:return{kind:`logged-in`};case`logged-out`:return{kind:`failed`};case`cli-missing`:return{kind:`cli-missing`};case`unavailable`:return{kind:`unavailable`};default:return d}}export{runLoginFlow};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AI_GATEWAY_API_KEY_ENV_VAR } from "../ai-gateway-api-key.js";
|
|
1
2
|
import { type SelectModelDeps } from "../boxes/select-model.js";
|
|
2
3
|
import { type ProjectDetectionOptions } from "../project-resolution.js";
|
|
3
4
|
import type { ModelRouting } from "#shared/agent-definition.js";
|
|
@@ -47,7 +48,7 @@ export type ModelProviderStatus = {
|
|
|
47
48
|
teamName?: string;
|
|
48
49
|
} | {
|
|
49
50
|
kind: "gateway-key";
|
|
50
|
-
envKey:
|
|
51
|
+
envKey: typeof AI_GATEWAY_API_KEY_ENV_VAR | "VERCEL_OIDC_TOKEN";
|
|
51
52
|
envFile: string;
|
|
52
53
|
};
|
|
53
54
|
/**
|
|
@@ -59,7 +60,7 @@ export type ModelProviderStatus = {
|
|
|
59
60
|
* disk — so it never surfaces as an outcome.
|
|
60
61
|
*/
|
|
61
62
|
export interface ModelProviderOutcome {
|
|
62
|
-
credential?: "VERCEL_OIDC_TOKEN" |
|
|
63
|
+
credential?: "VERCEL_OIDC_TOKEN" | typeof AI_GATEWAY_API_KEY_ENV_VAR;
|
|
63
64
|
status: ModelProviderStatus;
|
|
64
65
|
}
|
|
65
66
|
export type ModelFlowResult = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{interactiveAsker}from"../ask.js";import{detectProjectIdentity}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{findEnvFileWithKey}from"../boxes/detect-ai-gateway.js";import{fetchGatewayCatalog,selectModel}from"../boxes/select-model.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";import{runVercelFlow}from"./vercel.js";import{join}from"node:path";import{discoverAgent}from"#discover/discover-agent.js";import{inspectApplication}from"#services/inspect-application.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{createCompiledRuntimeModelCatalogLoader}from"#compiler/model-catalog.js";import{createStaticSourceChange}from"#source-change/static-source-change.js";var import_picocolors=__toESM(require_picocolors(),1);const MODEL_MENU_MESSAGE=``;function providerStatusHint(e,t=e=>e){return e.kind===`gateway-project`?`AI Gateway (Linked to ${e.teamName===void 0?t(e.projectName):`${t(e.projectName)} in ${t(e.teamName)}`})`:`AI Gateway (${e.envKey} in ${e.envFile})`}function modelMenuRows(e,t,n,r){let i;r?(i={value:`model`,label:`Change model`},e!==null&&(i.hint=e)):i={value:`model`,label:`Change model`,disabled:!0,description:`Set via an SDK model call in agent.ts; edit the source to change it`};let a;return a=n?.kind===`external`?{value:`provider`,label:`Change provider`,disabled:!0,description:`Disabled in external endpoint mode`}:t.kind===`unset`?{value:`provider`,label:import_picocolors.default.bold(import_picocolors.default.yellow(`Configure provider`)),hint:`Required to enable the agent`,accent:`warning`}:{value:`provider`,label:`Change provider`,hint:providerStatusHint(t,import_picocolors.default.bold)},[i,a,{value:`done`,label:`Done`}]}async function detectModelProviderStatus(e,t={}){let[
|
|
1
|
+
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{AI_GATEWAY_API_KEY_ENV_VAR}from"../ai-gateway-api-key.js";import{interactiveAsker}from"../ask.js";import{detectProjectIdentity}from"../project-resolution.js";import{snapshotSetupState}from"../state.js";import{WizardCancelledError}from"../step.js";import{withSpinner}from"../with-spinner.js";import{findEnvFileWithKey}from"../boxes/detect-ai-gateway.js";import{fetchGatewayCatalog,selectModel}from"../boxes/select-model.js";import{runInteractive}from"../runner.js";import{inProjectSetupState,prompterSink}from"./in-project.js";import{runVercelFlow}from"./vercel.js";import{join}from"node:path";import{discoverAgent}from"#discover/discover-agent.js";import{inspectApplication}from"#services/inspect-application.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{createCompiledRuntimeModelCatalogLoader}from"#compiler/model-catalog.js";import{createStaticSourceChange}from"#source-change/static-source-change.js";var import_picocolors=__toESM(require_picocolors(),1);const MODEL_MENU_MESSAGE=``;function providerStatusHint(e,t=e=>e){return e.kind===`gateway-project`?`AI Gateway (Linked to ${e.teamName===void 0?t(e.projectName):`${t(e.projectName)} in ${t(e.teamName)}`})`:`AI Gateway (${e.envKey} in ${e.envFile})`}function modelMenuRows(e,t,n,r){let i;r?(i={value:`model`,label:`Change model`},e!==null&&(i.hint=e)):i={value:`model`,label:`Change model`,disabled:!0,description:`Set via an SDK model call in agent.ts; edit the source to change it`};let a;return a=n?.kind===`external`?{value:`provider`,label:`Change provider`,disabled:!0,description:`Disabled in external endpoint mode`}:t.kind===`unset`?{value:`provider`,label:import_picocolors.default.bold(import_picocolors.default.yellow(`Configure provider`)),hint:`Required to enable the agent`,accent:`warning`}:{value:`provider`,label:`Change provider`,hint:providerStatusHint(t,import_picocolors.default.bold)},[i,a,{value:`done`,label:`Done`}]}async function detectModelProviderStatus(e,t={}){let[r,a,o]=await Promise.all([detectProjectIdentity(e,t),findEnvFileWithKey(e,AI_GATEWAY_API_KEY_ENV_VAR),findEnvFileWithKey(e,`VERCEL_OIDC_TOKEN`)]);if(r!==void 0){let e={kind:`gateway-project`,projectName:r.projectName};return r.teamName!==void 0&&(e.teamName=r.teamName),e}return a===void 0?o===void 0?{kind:`unset`}:{kind:`gateway-key`,envKey:`VERCEL_OIDC_TOKEN`,envFile:o}:{kind:`gateway-key`,envKey:AI_GATEWAY_API_KEY_ENV_VAR,envFile:a}}async function runModelFlow(e){let{appRoot:t,prompter:n,signal:r}=e,i={readCurrentModel:readCurrentAgentModel,applyModel:changeAgentModel,detectProviderStatus:detectModelProviderStatus,runVercelFlow,...e.deps},detectProvider=()=>i.detectProviderStatus(t,{signal:r}),[{id:a,routing:s,editable:c},l]=await withSpinner(n,`Checking the project…`,()=>Promise.all([i.readCurrentModel(t),detectProvider()]));r?.throwIfAborted();let u,d,f=s?.kind===`external`?{tone:`warning`,text:"`agent.ts` specifies a model provider directly. In-TUI configuration is restricted to AI Gateway endpoints."}:void 0,m=l.kind===`unset`&&s?.kind!==`external`?`provider`:c?`model`:s?.kind===`external`?`done`:`provider`,h=s?.kind!==`external`&&(e.initialStep===`provider`||l.kind===`unset`);for(;;){let e;if(h)h=!1,e=`provider`;else try{e=await n.select({message:``,options:modelMenuRows(a,l,s,c),hintLayout:`stacked`,initialValue:m,notices:f===void 0?[]:[f]})}catch(e){if(!(e instanceof WizardCancelledError))throw e;break}if(e===`done`)break;if(e===`model`){let e=await pickModelFromCatalog({appRoot:t,prompter:n,current:a,signal:r,deps:i.selectModel});if(e===void 0){m=`model`;continue}r?.throwIfAborted(),u=await i.applyModel({appRoot:t,slug:e}),r?.throwIfAborted();break}let p=await i.runVercelFlow({appRoot:t,prompter:n,signal:r});if(r?.throwIfAborted(),p.kind===`cancelled`){m=`provider`;continue}if(`outcome`in p){m=`done`;continue}l=await withSpinner(n,`Checking the project…`,detectProvider),r?.throwIfAborted(),d={status:l},p.credential!==void 0&&(d.credential=p.credential);break}if(u===void 0&&d===void 0)return{kind:`cancelled`};let g={kind:`done`};return u!==void 0&&(g.modelMessage=formatApplyModelOutcome(u)),d!==void 0&&(g.providerOutcome=d),g}async function pickModelFromCatalog(e){let{appRoot:t,prompter:n,current:i,signal:o}=e,s=e.deps?.fetchModels??fetchGatewayCatalog,p={asker:interactiveAsker(n),deps:{fetchModels:e=>withSpinner(n,`Loading the model catalog...`,()=>s(e))}};i!==null&&(p.defaultModel=i);let m=await runInteractive([selectModel(p)],inProjectSetupState(t,{kind:`unresolved`}),prompterSink(n),{snapshot:snapshotSetupState,signal:o});return m.kind===`cancelled`?void 0:m.state.modelId}function formatApplyModelOutcome(e){switch(e.kind){case`changed`:return`Model changed to ${import_picocolors.default.bold(e.to)}. Live on your next prompt.`;case`unchanged`:return`Model is already \`${e.model}\`.`;case`rejected`:return e.message}}async function changeAgentModel(e){let{appRoot:t,slug:n}=e,r=await validateModelSlug(t,n);if(r!==null)return{kind:`rejected`,message:r};let{manifest:i}=await discoverAgent({agentRoot:join(t,`agent`),appRoot:t}),a=await createStaticSourceChange(i).updateModelName(n);return a.kind===`bail`?{kind:`rejected`,message:`Couldn't edit ${a.at.logicalPath}: ${a.reason}. Change \`model\` by hand.`}:a.from===a.to?{kind:`unchanged`,model:a.to}:{kind:`changed`,to:a.to}}async function validateModelSlug(e,t){if(!t.includes(`/`))return`\`${t}\` isn't a provider/model id (e.g. anthropic/claude-sonnet-4.6).`;let n=createCompiledRuntimeModelCatalogLoader(e);try{if(await n.getModelLimits(formatLanguageModelGatewayId(t))===null)return`I couldn't confirm \`${t}\` in the AI Gateway model catalog, so I didn't change agent.ts.`}catch{return null}return null}async function readCurrentAgentModel(e){try{let{compiledState:t}=await inspectApplication(e),n=t?.manifest.config.model;return{id:n?.id??null,routing:n?.routing??null,editable:n!==void 0&&n.source===void 0}}catch{return{id:null,routing:null,editable:!1}}}async function modelChangeRefusalForUneditableModel(e){let{editable:t,routing:n}=await readCurrentAgentModel(e);return t?null:`Model is set via ${n?.kind===`external`?`the external provider \`${n.provider}\``:`an SDK model call`} in agent.ts, not a string literal; /model can't rewrite it. Edit \`model\` in agent.ts.`}export{MODEL_MENU_MESSAGE,changeAgentModel,detectModelProviderStatus,formatApplyModelOutcome,modelChangeRefusalForUneditableModel,runModelFlow};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{appendEnv}from"../append-env.js";import{WizardCancelledError}from"../step.js";import{getVercelAuthStatus}from"../vercel-project.js";import{runLinkFlow}from"./link.js";import{validateGatewayApiKey}from"../validate-gateway-key.js";
|
|
2
|
-
`),EXTERNAL_PROVIDER_INSTRUCTIONS_TITLE),{kind:`done`,outcome:`external-provider`};if(l===`own-key`)for(;;){let e;try{e=await
|
|
1
|
+
import{__toESM}from"../../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{appendEnv}from"../append-env.js";import{AI_GATEWAY_API_KEY_ENV_FILE,AI_GATEWAY_API_KEY_ENV_VAR,writeAiGatewayApiKey}from"../ai-gateway-api-key.js";import{WizardCancelledError}from"../step.js";import{withSpinner}from"../with-spinner.js";import{getVercelAuthStatus}from"../vercel-project.js";import{runLinkFlow}from"./link.js";import{validateGatewayApiKey}from"../validate-gateway-key.js";var import_picocolors=__toESM(require_picocolors(),1);const PROVIDER_QUESTION=`Which model provider do you want to use?`,CONNECTION_QUESTION=`How do you want to connect to AI Gateway?`,EXTERNAL_PROVIDER_INSTRUCTIONS_TITLE=`Using another model provider`,EXTERNAL_PROVIDER_INSTRUCTIONS=[`Set your provider's API key in ${AI_GATEWAY_API_KEY_ENV_FILE} — e.g. ANTHROPIC_API_KEY or OPENAI_API_KEY.`,'In agent/agent.ts, set `model` to a provider-authored model — e.g. `anthropic("claude-opus-4.8")` from `@ai-sdk/anthropic`.',`See https://eve.dev/docs/agent-config for details.`,"A running `eve dev` reloads env files automatically — no restart needed."];function projectConnectionOption(e){let t={value:`project`,label:`Connect via a project`,hint:`vercel link + env pull`};switch(e){case`authenticated`:return t;case`cli-missing`:return{...t,disabled:!0,disabledReason:`Vercel CLI not found, see /vc`,disabledReasonTone:`warning`};case`logged-out`:return{...t,disabled:!0,disabledReason:`Log in to Vercel first, see /login`,disabledReasonTone:`warning`};case`unavailable`:return{...t,disabled:!0,disabledReason:`Couldn't reach Vercel, check your connection`,disabledReasonTone:`warning`};default:return e}}async function runVercelFlow(e){let{appRoot:t,prompter:r,signal:o}=e,s={getVercelAuthStatus,runLinkFlow,appendEnv,validateGatewayApiKey,...e.deps},c,l;try{if(c=await r.select({message:PROVIDER_QUESTION,options:[{value:`gateway`,label:`Vercel AI Gateway`,hint:`one key, every model`},{value:`other`,label:`Something else`,hint:`use your own provider credentials`}],hintLayout:`stacked`}),c===`gateway`){let e=await withSpinner(r,`Checking your Vercel login…`,async()=>{let e=await s.getVercelAuthStatus(t,{signal:o});return o?.throwIfAborted(),e});l=await r.select({message:CONNECTION_QUESTION,options:[projectConnectionOption(e),{value:`own-key`,label:`Use my own key`,hint:`paste an ${AI_GATEWAY_API_KEY_ENV_VAR}`}],hintLayout:`stacked`})}}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}if(c===`other`)return r.acknowledge?await r.acknowledge({message:EXTERNAL_PROVIDER_INSTRUCTIONS_TITLE,lines:EXTERNAL_PROVIDER_INSTRUCTIONS}):r.note(EXTERNAL_PROVIDER_INSTRUCTIONS.join(`
|
|
2
|
+
`),EXTERNAL_PROVIDER_INSTRUCTIONS_TITLE),{kind:`done`,outcome:`external-provider`};if(l===`own-key`)for(;;){let e;try{e=await r.password({message:`Enter your ${AI_GATEWAY_API_KEY_ENV_VAR}`,validate:e=>e.trim().length===0?`API key cannot be empty.`:void 0})}catch(e){if(e instanceof WizardCancelledError)return{kind:`cancelled`};throw e}o?.throwIfAborted();let n=e.trim(),i=await withSpinner(r,`Validating…`,()=>s.validateGatewayApiKey(n,o));if(o?.throwIfAborted(),i.kind===`invalid`){r.log.error(`${i.message} Check the key and try again, or Esc to cancel.`);continue}i.kind===`inconclusive`?r.log.warning(`Couldn't reach the gateway to validate (${i.message}). Saving the key anyway.`):r.log.success(`${import_picocolors.default.green(`✓`)} ${import_picocolors.default.bold(`Valid key`)}`);let a=await writeAiGatewayApiKey({projectRoot:t,apiKey:n,appendEnv:s.appendEnv});return o?.throwIfAborted(),r.log.success(`Saved ${a.envKey} to ${a.envFile}.`),{kind:`done`,credential:AI_GATEWAY_API_KEY_ENV_VAR}}else return await s.runLinkFlow({appRoot:t,prompter:r,signal:o,projectSelection:`create-or-link`})}export{CONNECTION_QUESTION,EXTERNAL_PROVIDER_INSTRUCTIONS,EXTERNAL_PROVIDER_INSTRUCTIONS_TITLE,PROVIDER_QUESTION,runVercelFlow};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"../update/package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{join}from"node:path";import{readFile}from"node:fs/promises";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(i){let a=join(i.projectRoot,`package.json`);if(!await pathExists(a))throw Error(`Cannot add an eve agent to "${i.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let o=agentTemplateFiles(i.model),s=[];for(let e of Object.keys(o))await pathExists(join(i.projectRoot,e))&&s.push(e);if(s.length===0&&await pathExists(join(i.projectRoot,`agent`))&&s.push(`agent/`),s.length>0)throw Error(`Cannot add an eve agent to "${i.projectRoot}" because it already has: ${s.join(`, `)}
|
|
1
|
+
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pathExists,writeTextFile}from"../files.js";import{patchPackageJson}from"../update/package-json.js";import{resolveVersionToken}from"../version-tokens.js";import{agentTemplateFiles,formatEveDependencySpecifier,resolveEvePackageContract}from"./project.js";import{join}from"node:path";import{readFile}from"node:fs/promises";const DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`];function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function hasDeclaredDependency(e,t){if(!isJsonObject(e))return!1;for(let n of DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function addAgentToProject(i){let a=join(i.projectRoot,`package.json`);if(!await pathExists(a))throw Error(`Cannot add an eve agent to "${i.projectRoot}" because it has no package.json. Run \`eve init <name>\` to create a new project instead.`);let o=agentTemplateFiles(i.model),s=[];for(let e of Object.keys(o))await pathExists(join(i.projectRoot,e))&&s.push(e);if(s.length===0&&await pathExists(join(i.projectRoot,`agent`))&&s.push(`agent/`),s.length>0)throw Error(`Cannot add an eve agent to "${i.projectRoot}" because it already has: ${s.join(`, `)}.`);let c=resolveEvePackageContract(i.evePackage),l=resolveVersionToken(`aiPackageVersion`,i.aiPackageVersion??`7.0.0-beta.178`),u=resolveVersionToken(`connectPackageVersion`,i.connectPackageVersion??`0.2.2`),d=resolveVersionToken(`zodPackageVersion`,i.zodPackageVersion??`4.4.3`),f=[];for(let[e,t]of Object.entries(o)){let r=join(i.projectRoot,e);await writeTextFile(r,t),f.push(r)}let p=JSON.parse(await readFile(a,`utf8`)),m={"@vercel/connect":u,ai:l,eve:formatEveDependencySpecifier(c.version),zod:d},h={};for(let[e,t]of Object.entries(m))hasDeclaredDependency(p,e)||(h[e]=t);let g={nodeEngineRequirement:c.nodeEngine};Object.keys(h).length>0&&(g.dependencies=h);let _=await patchPackageJson(a,g);return await getPackageManagerStrategy(i.packageManager??`pnpm`).applyProjectConfiguration(i.projectRoot),{filesWritten:f,dependenciesAdded:Object.keys(h).sort(),nodeEngineOverride:_.nodeEngineOverride}}export{addAgentToProject};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.12.
|
|
1
|
+
import{getPackageManagerStrategy}from"../../primitives/pm/index.js";import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`7.0.0-beta.178`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.12.3`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
4
|
model: "__EVE_INIT_MODEL__",
|
|
@@ -28,12 +28,6 @@ export interface PickProjectOptions extends VercelProjectOperationOptions {
|
|
|
28
28
|
allowCreateWhenEmpty?: boolean;
|
|
29
29
|
}
|
|
30
30
|
export declare function unresolvedProject(): ProjectResolution;
|
|
31
|
-
/**
|
|
32
|
-
* Runs a network reach behind a section-like spinner so the user sees the CLI
|
|
33
|
-
* is working, not hung. The spinner clears whether the work resolves or throws,
|
|
34
|
-
* and degrades to nothing when the prompter has no spinner (headless/test).
|
|
35
|
-
*/
|
|
36
|
-
export declare function withNetworkSpinner<T>(prompter: Prompter, message: string, task: () => Promise<T>): Promise<T>;
|
|
37
31
|
/** Resolves the linked project id from a resolution, if any. */
|
|
38
32
|
export declare function projectIdFromResolution(project: ProjectResolution): string | undefined;
|
|
39
33
|
/** Lists the Vercel scopes available to the current CLI user. */
|
|
@@ -66,6 +60,8 @@ export declare function requireVercelAuth(failure: VercelCaptureFailure): never;
|
|
|
66
60
|
* not-authenticated diagnostic.
|
|
67
61
|
*/
|
|
68
62
|
export type VercelAuthStatus = "authenticated" | "logged-out" | "cli-missing" | "unavailable";
|
|
63
|
+
/** Returns the user-facing reason Vercel-backed setup is unavailable. */
|
|
64
|
+
export declare function vercelAuthBlockerReason(authStatus: VercelAuthStatus): string | undefined;
|
|
69
65
|
export declare function getVercelAuthStatus(projectRoot: string, options?: VercelProjectOperationOptions): Promise<VercelAuthStatus>;
|
|
70
66
|
/**
|
|
71
67
|
* Throws the re-auth action for a forbidden scope. The session is logged in,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{__toESM}from"../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{boolean,object,record,string,unknown}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{hasVercelHostFramework}from"#setup/scaffold/index.js";import{captureVercel,runVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{whimsyFor}from"#setup/cli/index.js";var import_picocolors=__toESM(require_picocolors(),1);const JsonObjectSchema=record(string(),unknown()),VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelProjectReferenceSchema=object({id:string(),name:string()}),VercelApiErrorSchema=object({error:object({code:string().optional(),message:string().optional()}).optional()});function unresolvedProject(){return{kind:`unresolved`}}async function withNetworkSpinner(e,t,n){let r=e.log.spinner?.(t);try{return await n()}finally{r?.stop()}}function projectIdFromResolution(e){return e.kind===`unresolved`?void 0:e.projectId}function parseJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}function parseVercelJsonList(e,t,n){let r=parseJson(e,t),i=JsonObjectSchema.safeParse(r);if(!i.success)throw Error(`Could not read ${t} from Vercel CLI JSON output.`);let a=i.data[t];if(!Array.isArray(a))throw Error(`Vercel CLI JSON output did not include ${t}.`);let o=[];for(let e of a){let t=n.safeParse(e);t.success&&o.push(t.data)}return o}function parseTeamList(e){return e===void 0?[]:parseVercelJsonList(e,`teams`,VercelTeamListEntrySchema)}async function listTeams(e,t={}){let n=await captureVercel([`teams`,`ls`,`--format`,`json`],{cwd:e,signal:t.signal});if(!n.ok)throw isForbiddenApiFailure(n.failure)&&requireVercelTeamAccess(n.failure),Error(`Could not list Vercel teams. ${n.failure.message}`);return parseTeamList(n.stdout)}function parseProjectList(e){return e===void 0?[]:parseVercelJsonList(e,`projects`,VercelProjectListEntrySchema)}async function listProjects(e,t,n={}){let r=await captureVercel([`project`,`ls`,`--format`,`json`,`--scope`,t],{cwd:e,signal:n.signal});if(!r.ok)throw isForbiddenApiFailure(r.failure)&&requireVercelTeamAccess(r.failure),Error(`Could not list Vercel projects in ${t}. ${r.failure.message}`);return parseProjectList(r.stdout)}function isNotFoundApiFailure(e){if(e.code===404)return!0;let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stderr}`.toLowerCase();return r.includes(`not_found`)||r.includes(`not found`)||r.includes(`404`)}function isConflictApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`409`)||r.includes(`conflict`)||r.includes(`already exists`)}function isForbiddenApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`403`)||r.includes(`forbidden`)||r.includes(`not_authorized`)||r.includes(`not authorized`)||r.includes(`sso`)||r.includes(`saml`)}function safeParseJson(e){try{return JSON.parse(e)}catch{return}}function parseProjectReference(e,t){let n=VercelProjectReferenceSchema.safeParse(parseJson(e,t));if(!n.success)throw Error(`Could not read Vercel project identity from ${t}.`);return n.data}async function resolveProjectByNameOrId(e,t,n,r={}){let i=await captureVercel([`api`,`/v9/projects/${encodeURIComponent(n)}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal});if(i.ok)return parseProjectReference(i.stdout,`project ${n}`);if(isNotFoundApiFailure(i.failure))return null;throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not resolve project "${n}" in ${t}. ${i.failure.message}`)}async function createProject(e,t,n,r,i){let a=[`api`,`/v10/projects`,`--scope`,t,`--method`,`POST`,`--raw-field`,`name=${n}`];await hasVercelHostFramework(e)||a.push(`--raw-field`,`framework=eve`),a.push(`--raw`);let o=await captureVercel(a,{cwd:e,onOutput:r,signal:i.signal});if(o.ok)return parseProjectReference(o.stdout,`created project ${n}`);throw isConflictApiFailure(o.failure)?Error(projectNameCollisionMessage(n,t)):(isForbiddenApiFailure(o.failure)&&requireVercelTeamAccess(o.failure),Error(`Could not create Vercel project "${n}" in ${t}. ${o.failure.message}`))}function projectNameCollisionMessage(e,t){return`Vercel project "${e}" already exists in ${t}. Pass --project ${e} to link it, or choose a different project name.`}async function assertNewProjectNameAvailable(e,t,n,r={}){if(await resolveProjectByNameOrId(e,t,n,r)!==null)throw Error(projectNameCollisionMessage(n,t))}function requireVercelLogin(e){let t=`Provisioning a Vercel project requires you to be logged in to Vercel.`,n=e?.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-login`,command:`vercel login`,reason:e?`${t} The Vercel CLI check did not succeed: ${e.message}${n?` ${n}`:``}`:t})}function probeWhoami(e,t){return captureVercel([`whoami`],{cwd:e,signal:t.signal,timeoutMs:1e4})}function isLoggedOutFailure(e){let t=`${e.stdout} ${e.stderr}`.toLowerCase();return t.includes(`credentials`)||t.includes(`not authenticated`)||t.includes(`not logged in`)}function requireVercelAuth(e){if(e.errno===`ENOENT`)throw new HumanActionRequiredError({kind:`vercel-cli-missing`,command:`npm i -g vercel@latest`,reason:e.message});isLoggedOutFailure(e)&&requireVercelLogin(e);let t=e.stderr.trim();throw Error(`Couldn't verify your Vercel login: ${e.message}${t?` ${t}`:``}`)}async function getVercelAuthStatus(e,t={}){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok?`authenticated`:n.failure.errno===`ENOENT`?`cli-missing`:isLoggedOutFailure(n.failure)?`logged-out`:`unavailable`}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.`})}async function requireAuth(e,t,n={}){let check=async()=>{let t=await probeWhoami(e,n);n.signal?.throwIfAborted(),t.ok||requireVercelAuth(t.failure)};if(t===void 0){await check();return}await withNetworkSpinner(t,whimsyFor(`auth`),check)}async function isVercelAuthenticated(e,t={}){return await getVercelAuthStatus(e,t)===`authenticated`}async function whoamiScope(e,t){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok||requireVercelAuth(n.failure),n.stdout.trim()}async function resolveTeam(e,t,n={}){return t===void 0?(await listTeams(e,n)).find(e=>e.current)?.slug??await whoamiScope(e,n):t}async function validateTeam(e,t,n,r={}){if(n===void 0)return;let i=await listTeams(t,r);if(i.length>0&&!i.some(e=>e.slug===n))throw Error(`Team "${n}" was not found in \`vercel teams ls\`. Pass a valid team slug or omit --team.`)}async function pickTeam(e,t,n,r={}){if(n!==void 0)return await validateTeam(e,t,n,r),resolveTeam(t,n,r);let i=await withNetworkSpinner(e,whimsyFor(`teams`),()=>listTeams(t,r));return i.length<=1?i.find(e=>e.current)?.slug??await whoamiScope(t,r):e.select({message:`Select your team`,search:!0,placeholder:`type to search teams`,options:i.map(e=>({value:e.slug,label:e.current?`${e.name} (current)`:e.name})),initialValue:i.find(e=>e.current)?.slug})}async function pickProject(e,t,n,r={}){let i=await withNetworkSpinner(e,whimsyFor(`projects`,n),()=>listProjects(t,n,r));if(i.length===0){if(r.allowCreateWhenEmpty===!1)throw Error(`No existing Vercel projects found in ${n}. Create one in Vercel, then try again.`);return{project:await e.text({message:`No projects found in ${n}. Enter a project name to create`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}),exists:!1}}return{project:await e.select({message:`Project to link`,search:!0,placeholder:`type to filter projects`,options:i.map(e=>({value:e.name,label:e.name}))}),exists:!0}}async function pickNewProjectName(e,t,n,r,i={}){let a=await withNetworkSpinner(e,whimsyFor(`project-name`,n),()=>resolveProjectByNameOrId(t,n,r.trim(),i)),o=r.trim();for(;a!==null;)o=(await e.text({message:`New project name`,defaultValue:`${o}-2`,notices:[{tone:`warning`,text:`${import_picocolors.default.yellow(`Project named`)} '${import_picocolors.default.blue(o)}' ${import_picocolors.default.yellow(`already exists in`)} '${import_picocolors.default.blue(n)}'`}],validate:e=>{if(e.trim().length===0)return`Project name cannot be empty.`}})).trim(),a=await resolveProjectByNameOrId(t,n,o,i);return o}async function linkProject(e,t,n,r,i={}){let a=[`--scope`,n.team],o;if(n.kind===`new`)o=await withNetworkSpinner(e,`Creating Vercel project "${n.project}" in ${n.team}...`,async()=>(await assertNewProjectNameAvailable(t,n.team,n.project,i),createProject(t,n.team,n.project,r,i)));else{let e=await resolveProjectByNameOrId(t,n.team,n.project,i);if(e===null)throw Error(`Vercel project "${n.project}" was not found in ${n.team}.`);o=e}return withNetworkSpinner(e,`Linking this directory to Vercel project "${o.name}"...`,()=>runVercel([`link`,`--project`,o.id,...a,`--yes`],{cwd:t,onOutput:r,nonInteractive:!0,signal:i.signal}))}export{assertNewProjectNameAvailable,getVercelAuthStatus,isVercelAuthenticated,linkProject,listProjects,listTeams,pickNewProjectName,pickProject,pickTeam,projectIdFromResolution,requireAuth,requireVercelAuth,requireVercelLogin,requireVercelTeamAccess,resolveProjectByNameOrId,resolveTeam,unresolvedProject,validateTeam,withNetworkSpinner};
|
|
1
|
+
import{__toESM}from"../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{boolean,object,record,string,unknown}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{withSpinner}from"./with-spinner.js";import{hasVercelHostFramework}from"#setup/scaffold/index.js";import{captureVercel,runVercel}from"#setup/primitives/index.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{whimsyFor}from"#setup/cli/index.js";var import_picocolors=__toESM(require_picocolors(),1);const JsonObjectSchema=record(string(),unknown()),VercelTeamListEntrySchema=object({name:string(),slug:string(),current:boolean()}),VercelProjectListEntrySchema=object({name:string(),id:string()}),VercelProjectReferenceSchema=object({id:string(),name:string()}),VercelApiErrorSchema=object({error:object({code:string().optional(),message:string().optional()}).optional()});function unresolvedProject(){return{kind:`unresolved`}}function projectIdFromResolution(e){return e.kind===`unresolved`?void 0:e.projectId}function parseJson(e,t){try{return JSON.parse(e)}catch{throw Error(`Could not parse ${t} JSON from Vercel CLI output.`)}}function parseVercelJsonList(e,t,n){let r=parseJson(e,t),i=JsonObjectSchema.safeParse(r);if(!i.success)throw Error(`Could not read ${t} from Vercel CLI JSON output.`);let a=i.data[t];if(!Array.isArray(a))throw Error(`Vercel CLI JSON output did not include ${t}.`);let o=[];for(let e of a){let t=n.safeParse(e);t.success&&o.push(t.data)}return o}function parseTeamList(e){return e===void 0?[]:parseVercelJsonList(e,`teams`,VercelTeamListEntrySchema)}async function listTeams(e,t={}){let n=await captureVercel([`teams`,`ls`,`--format`,`json`],{cwd:e,signal:t.signal});if(!n.ok)throw isForbiddenApiFailure(n.failure)&&requireVercelTeamAccess(n.failure),Error(`Could not list Vercel teams. ${n.failure.message}`);return parseTeamList(n.stdout)}function parseProjectList(e){return e===void 0?[]:parseVercelJsonList(e,`projects`,VercelProjectListEntrySchema)}async function listProjects(e,t,n={}){let r=await captureVercel([`project`,`ls`,`--format`,`json`,`--scope`,t],{cwd:e,signal:n.signal});if(!r.ok)throw isForbiddenApiFailure(r.failure)&&requireVercelTeamAccess(r.failure),Error(`Could not list Vercel projects in ${t}. ${r.failure.message}`);return parseProjectList(r.stdout)}function isNotFoundApiFailure(e){if(e.code===404)return!0;let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stderr}`.toLowerCase();return r.includes(`not_found`)||r.includes(`not found`)||r.includes(`404`)}function isConflictApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`409`)||r.includes(`conflict`)||r.includes(`already exists`)}function isForbiddenApiFailure(e){let t=VercelApiErrorSchema.safeParse(safeParseJson(e.stdout)),n=t.success?t.data.error:void 0,r=`${n?.code??``} ${n?.message??``} ${e.stdout} ${e.stderr}`.toLowerCase();return r.includes(`403`)||r.includes(`forbidden`)||r.includes(`not_authorized`)||r.includes(`not authorized`)||r.includes(`sso`)||r.includes(`saml`)}function safeParseJson(e){try{return JSON.parse(e)}catch{return}}function parseProjectReference(e,t){let n=VercelProjectReferenceSchema.safeParse(parseJson(e,t));if(!n.success)throw Error(`Could not read Vercel project identity from ${t}.`);return n.data}async function resolveProjectByNameOrId(e,t,n,r={}){let i=await captureVercel([`api`,`/v9/projects/${encodeURIComponent(n)}`,`--scope`,t,`--raw`],{cwd:e,signal:r.signal});if(i.ok)return parseProjectReference(i.stdout,`project ${n}`);if(isNotFoundApiFailure(i.failure))return null;throw isForbiddenApiFailure(i.failure)&&requireVercelTeamAccess(i.failure),Error(`Could not resolve project "${n}" in ${t}. ${i.failure.message}`)}async function createProject(e,t,n,r,i){let a=[`api`,`/v10/projects`,`--scope`,t,`--method`,`POST`,`--raw-field`,`name=${n}`];await hasVercelHostFramework(e)||a.push(`--raw-field`,`framework=eve`),a.push(`--raw`);let o=await captureVercel(a,{cwd:e,onOutput:r,signal:i.signal});if(o.ok)return parseProjectReference(o.stdout,`created project ${n}`);throw isConflictApiFailure(o.failure)?Error(projectNameCollisionMessage(n,t)):(isForbiddenApiFailure(o.failure)&&requireVercelTeamAccess(o.failure),Error(`Could not create Vercel project "${n}" in ${t}. ${o.failure.message}`))}function projectNameCollisionMessage(e,t){return`Vercel project "${e}" already exists in ${t}. Pass --project ${e} to link it, or choose a different project name.`}async function assertNewProjectNameAvailable(e,t,n,r={}){if(await resolveProjectByNameOrId(e,t,n,r)!==null)throw Error(projectNameCollisionMessage(n,t))}function requireVercelLogin(e){let t=`Provisioning a Vercel project requires you to be logged in to Vercel.`,n=e?.stderr.trim();throw new HumanActionRequiredError({kind:`vercel-login`,command:`vercel login`,reason:e?`${t} The Vercel CLI check did not succeed: ${e.message}${n?` ${n}`:``}`:t})}function probeWhoami(e,t){return captureVercel([`whoami`],{cwd:e,signal:t.signal,timeoutMs:1e4})}function isLoggedOutFailure(e){let t=`${e.stdout} ${e.stderr}`.toLowerCase();return t.includes(`credentials`)||t.includes(`not authenticated`)||t.includes(`not logged in`)}function requireVercelAuth(e){if(e.errno===`ENOENT`)throw new HumanActionRequiredError({kind:`vercel-cli-missing`,command:`npm i -g vercel@latest`,reason:e.message});isLoggedOutFailure(e)&&requireVercelLogin(e);let t=e.stderr.trim();throw Error(`Couldn't verify your Vercel login: ${e.message}${t?` ${t}`:``}`)}function vercelAuthBlockerReason(e){switch(e){case`authenticated`:return;case`cli-missing`:return`Vercel CLI not found, see /vc`;case`logged-out`:return`Log in to Vercel first, see /login`;case`unavailable`:return`Couldn't reach Vercel, check your connection`;default:return e}}async function getVercelAuthStatus(e,t={}){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok?`authenticated`:n.failure.errno===`ENOENT`?`cli-missing`:isLoggedOutFailure(n.failure)?`logged-out`:`unavailable`}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.`})}async function requireAuth(e,t,n={}){let check=async()=>{let t=await probeWhoami(e,n);n.signal?.throwIfAborted(),t.ok||requireVercelAuth(t.failure)};if(t===void 0){await check();return}await withSpinner(t,whimsyFor(`auth`),check)}async function isVercelAuthenticated(e,t={}){return await getVercelAuthStatus(e,t)===`authenticated`}async function whoamiScope(e,t){let n=await probeWhoami(e,t);return t.signal?.throwIfAborted(),n.ok||requireVercelAuth(n.failure),n.stdout.trim()}async function resolveTeam(e,t,n={}){return t===void 0?(await listTeams(e,n)).find(e=>e.current)?.slug??await whoamiScope(e,n):t}async function validateTeam(e,t,n,r={}){if(n===void 0)return;let i=await listTeams(t,r);if(i.length>0&&!i.some(e=>e.slug===n))throw Error(`Team "${n}" was not found in \`vercel teams ls\`. Pass a valid team slug or omit --team.`)}async function pickTeam(e,t,n,r={}){if(n!==void 0)return await validateTeam(e,t,n,r),resolveTeam(t,n,r);let i=await withSpinner(e,whimsyFor(`teams`),()=>listTeams(t,r));return i.length<=1?i.find(e=>e.current)?.slug??await whoamiScope(t,r):e.select({message:`Select your team`,search:!0,placeholder:`type to search teams`,options:i.map(e=>({value:e.slug,label:e.current?`${e.name} (current)`:e.name})),initialValue:i.find(e=>e.current)?.slug})}async function pickProject(e,t,n,r={}){let i=await withSpinner(e,whimsyFor(`projects`,n),()=>listProjects(t,n,r));if(i.length===0){if(r.allowCreateWhenEmpty===!1)throw Error(`No existing Vercel projects found in ${n}. Create one in Vercel, then try again.`);return{project:await e.text({message:`No projects found in ${n}. Enter a project name to create`,validate:e=>e.trim().length===0?`Project name cannot be empty.`:void 0}),exists:!1}}return{project:await e.select({message:`Project to link`,search:!0,placeholder:`type to filter projects`,options:i.map(e=>({value:e.name,label:e.name}))}),exists:!0}}async function pickNewProjectName(e,t,n,r,i={}){let a=await withSpinner(e,whimsyFor(`project-name`,n),()=>resolveProjectByNameOrId(t,n,r.trim(),i)),o=r.trim();for(;a!==null;)o=(await e.text({message:`New project name`,defaultValue:`${o}-2`,notices:[{tone:`warning`,text:`${import_picocolors.default.yellow(`Project named`)} '${import_picocolors.default.blue(o)}' ${import_picocolors.default.yellow(`already exists in`)} '${import_picocolors.default.blue(n)}'`}],validate:e=>{if(e.trim().length===0)return`Project name cannot be empty.`}})).trim(),a=await resolveProjectByNameOrId(t,n,o,i);return o}async function linkProject(e,t,n,r,i={}){let a=[`--scope`,n.team],o;if(n.kind===`new`)o=await withSpinner(e,`Creating Vercel project "${n.project}" in ${n.team}...`,async()=>(await assertNewProjectNameAvailable(t,n.team,n.project,i),createProject(t,n.team,n.project,r,i)));else{let e=await resolveProjectByNameOrId(t,n.team,n.project,i);if(e===null)throw Error(`Vercel project "${n.project}" was not found in ${n.team}.`);o=e}return withSpinner(e,`Linking this directory to Vercel project "${o.name}"...`,()=>runVercel([`link`,`--project`,o.id,...a,`--yes`],{cwd:t,onOutput:r,nonInteractive:!0,signal:i.signal}))}export{assertNewProjectNameAvailable,getVercelAuthStatus,isVercelAuthenticated,linkProject,listProjects,listTeams,pickNewProjectName,pickProject,pickTeam,projectIdFromResolution,requireAuth,requireVercelAuth,requireVercelLogin,requireVercelTeamAccess,resolveProjectByNameOrId,resolveTeam,unresolvedProject,validateTeam,vercelAuthBlockerReason};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function withSpinner(e,t,n){let r=e.log.spinner?.(t);try{return await n()}finally{r?.stop()}}export{withSpinner};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function stripNpmPackageScope(e){let t=e.lastIndexOf(`/`);return t===-1?e:e.slice(t+1)}export{stripNpmPackageScope};
|
package/docs/channels/slack.mdx
CHANGED
|
@@ -105,7 +105,7 @@ export default slackChannel({
|
|
|
105
105
|
|
|
106
106
|
HITL renders as Slack buttons and selects. When the user responds, the parked session (paused awaiting input) resumes.
|
|
107
107
|
|
|
108
|
-
Authorization prompts
|
|
108
|
+
Authorization prompts split public status from private credentials. A sign-in challenge (OAuth URL, device code) is a credential. Anyone who completes it binds their identity to the session's connection. The default `authorization.required` handler posts a public, link-free status in the thread, delivers the actual challenge ephemerally to the triggering user, device code included, and then updates that public status when `authorization.completed` fires. The handler receives a private-delivery context with `postEphemeral`, `postDirectMessage` (needs the `im:write` scope), and `state`. There is, intentionally, no public `post` and no raw API access.
|
|
109
109
|
|
|
110
110
|
```ts
|
|
111
111
|
events: {
|
package/docs/connections.mdx
CHANGED
|
@@ -124,11 +124,11 @@ import { defineMcpClientConnection } from "eve/connections";
|
|
|
124
124
|
export default defineMcpClientConnection({
|
|
125
125
|
url: "https://mcp.linear.app/sse",
|
|
126
126
|
description: "Linear workspace: issues, projects, cycles, and comments.",
|
|
127
|
-
auth: connect("linear"),
|
|
127
|
+
auth: connect("linear/myagent"),
|
|
128
128
|
});
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
`"linear"` is the UID you chose when registering the Connect client. Connect-managed OAuth is user-scoped by default, so the runtime resolves the per-user token before each tool call. The full setup (Connect client provisioning, project linking, the runtime consent flow) lives in [Auth & route protection](./guides/auth-and-route-protection).
|
|
131
|
+
`"linear/myagent"` is the UID you chose when registering the Connect client. Connect-managed OAuth is user-scoped by default, so the runtime resolves the per-user token before each tool call. The full setup (Connect client provisioning, project linking, the runtime consent flow) lives in [Auth & route protection](./guides/auth-and-route-protection).
|
|
132
132
|
|
|
133
133
|
## Self-hosted interactive OAuth
|
|
134
134
|
|
|
@@ -183,7 +183,7 @@ export default defineMcpClientConnection({
|
|
|
183
183
|
|
|
184
184
|
Drop `resume` when the provider keeps flow state server-side, so nothing has to cross the step boundary.
|
|
185
185
|
|
|
186
|
-
`displayName` is presentation-only. The connection's path-derived name still keys the authorization
|
|
186
|
+
`displayName` is presentation-only. The connection's path-derived name still keys the authorization flow, token cache, and callback URL. You can also set `displayName` on the `auth` definition itself (e.g. `auth: { ...connect("salesforce/myagent"), displayName: "Salesforce" }`); that definition-level value wins over one the strategy stamps on the challenge, and channels fall back to title-casing the connection name when neither is set.
|
|
187
187
|
|
|
188
188
|
### Signaling authorization state
|
|
189
189
|
|
|
@@ -205,23 +205,26 @@ To narrow a caught error, use `isConnectionAuthorizationRequiredError(err)` and
|
|
|
205
205
|
|
|
206
206
|
### Handling a revoked token mid-call
|
|
207
207
|
|
|
208
|
-
`getToken` only runs _before_ a tool call, so a grant revoked while a tool is mid-flight first surfaces as a downstream `401` inside your `execute`. A plain throw there is only a tool error, so the model sees a failure and the cached bearer sticks around. Instead, map a provider `401` to `ctx.requireAuth()
|
|
208
|
+
`getToken` only runs _before_ a tool call, so a grant revoked while a tool is mid-flight first surfaces as a downstream `401` inside your `execute`. A plain throw there is only a tool error, so the model sees a failure and the cached bearer sticks around. Instead, map a provider `401` to `ctx.requireAuth(provider)`. eve then evicts the rejected token from its per-step cache and re-runs the consent flow with a fresh one, exactly as it does for a connection whose server rejects the bearer.
|
|
209
209
|
|
|
210
210
|
```ts title="agent/tools/list_issues.ts"
|
|
211
|
+
import { connect } from "@vercel/connect/eve";
|
|
211
212
|
import { defineTool } from "eve/tools";
|
|
212
213
|
import { z } from "zod";
|
|
213
214
|
|
|
215
|
+
const linearAuth = connect("linear/myagent");
|
|
216
|
+
|
|
214
217
|
export default defineTool({
|
|
215
218
|
description: "List open Linear issues.",
|
|
216
219
|
inputSchema: z.object({}),
|
|
217
220
|
async execute(_input, ctx) {
|
|
218
|
-
const { token } = await ctx.getToken();
|
|
221
|
+
const { token } = await ctx.getToken(linearAuth);
|
|
219
222
|
const res = await fetch("https://api.linear.app/graphql", {
|
|
220
223
|
headers: { authorization: `Bearer ${token}` },
|
|
221
224
|
});
|
|
222
225
|
// The grant was revoked since getToken ran: re-challenge instead of
|
|
223
226
|
// returning a dead-token error to the model.
|
|
224
|
-
if (res.status === 401) ctx.requireAuth();
|
|
227
|
+
if (res.status === 401) ctx.requireAuth(linearAuth);
|
|
225
228
|
return await res.json();
|
|
226
229
|
},
|
|
227
230
|
});
|
package/docs/getting-started.mdx
CHANGED
|
@@ -38,10 +38,15 @@ The command:
|
|
|
38
38
|
|
|
39
39
|
- Creates a child directory using the current workspace or launcher package manager, and uses eve's default model
|
|
40
40
|
- Installs dependencies and initializes Git
|
|
41
|
-
-
|
|
41
|
+
- When a supported coding-agent REPL (Claude Code, Codex, Cursor, Droid, Gemini CLI, opencode, or Pi) is on `PATH`, asks whether to open an available REPL or start the development server; otherwise starts the server and opens the interactive [terminal UI](./guides/dev-tui)
|
|
42
42
|
|
|
43
43
|
Type a message and watch the model loop run. Pass `--channel-web-nextjs` to add the Web Chat application. Every app ships the built-in HTTP channel (`agent/channels/eve.ts`) regardless.
|
|
44
44
|
|
|
45
|
+
When you choose one of those REPLs, eve starts it with a project-specific
|
|
46
|
+
prompt that guides the setup. The prompt distinguishes `eve dev`, which starts
|
|
47
|
+
eve's HMR server and the agent's terminal REPL, from `eve dev --no-ui`, which is
|
|
48
|
+
the controllable background mode for verification.
|
|
49
|
+
|
|
45
50
|
`eve init` holds the terminal, so stop it with Ctrl+C to get your shell back before editing the generated agent. The command does not create a Vercel project or deploy.
|
|
46
51
|
|
|
47
52
|
To add eve to an existing project, run `eve init .` from a directory that already has a `package.json` and no `agent/` files yet. eve adds the missing `eve`, `ai`, and `zod` dependencies without touching anything else the project owns. The eve dependency and the Node engine come from the same release. eve pins `engines.node` to the lowest major that release supports (for example `24.x`). It keeps an existing range only when every version that range allows stays within that major; otherwise it replaces the range and prints a warning.
|
|
@@ -186,10 +191,10 @@ See [Sessions, runs and streaming](./concepts/sessions-runs-and-streaming) for t
|
|
|
186
191
|
|
|
187
192
|
If a coding agent (Claude Code, Cursor, and the like) is doing the setup, hand it this prompt:
|
|
188
193
|
|
|
189
|
-
<CopyPrompt text="Set up an eve agent for the user. eve is a filesystem-first TypeScript framework for durable agents, published as the npm package eve. Read its docs: once eve is installed they are bundled in the package at node_modules/eve/docs; before eve is installed, read the published Introduction and Getting Started pages. If the project has no eve app, scaffold one with `npx eve@latest init <name>`; add `--channel-web-nextjs` only when the user wants Web Chat.
|
|
194
|
+
<CopyPrompt text="Set up an eve agent for the user. eve is a filesystem-first TypeScript framework for durable agents, published as the npm package eve. Read its docs: once eve is installed they are bundled in the package at node_modules/eve/docs; before eve is installed, read the published Introduction and Getting Started pages. If the project has no eve app, scaffold one with `npx eve@latest init <name>`; add `--channel-web-nextjs` only when the user wants Web Chat. In a coding-agent launch, init installs dependencies and prints the project-specific dev command instead of starting the interactive terminal UI. A fresh project also initializes Git; an existing app keeps its repository and scripts. To add eve to an existing app, run `eve init .`, or install the dependencies by hand with `npm install eve@latest ai zod` (init adds ai and zod; the by-hand path needs all three). Make sure agent/agent.ts and agent/instructions.md exist, then add a first typed tool at agent/tools/get_weather.ts using defineTool from eve/tools with a Zod inputSchema and an inline execute. Start eve in a controllable background process with `npx eve dev --no-ui`, wait for the server URL, then exercise the HTTP API: create a session with POST /eve/v1/session, attach to GET /eve/v1/session/:id/stream, and send a follow-up with the returned continuationToken. Stop the dev process after verification. Verify with the project's typecheck, adapt model and provider choices to the project, and do not commit unless the user asks.">
|
|
190
195
|
Set up an eve agent: read the eve docs (bundled at node_modules/eve/docs once eve is
|
|
191
196
|
installed), scaffold with `npx eve@latest init <name>` (or `npm install eve@latest ai zod` in an existing app), add
|
|
192
|
-
a typed tool at agent/tools/get_weather.ts, run it with `
|
|
197
|
+
a typed tool at agent/tools/get_weather.ts, run it with `npx eve dev --no-ui`, then create a session, stream
|
|
193
198
|
it, and send a follow-up.
|
|
194
199
|
</CopyPrompt>
|
|
195
200
|
|