eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
2 lines • 9.54 kB
JavaScript
import{SkippedSignal,confirm}from"../ask.js";import{detectDeployment,isProjectResolved,mergeProjectResolution,projectResolutionFromDeployment}from"../project-resolution.js";import{provisionSlackbot,reconcileSlackUid}from"../slackbot.js";import{hasVercelProject,requireProjectPath}from"../state.js";import{WizardCancelledError}from"../step.js";import{deriveSlackConnectorSlug,ensureChannel}from"#setup/scaffold/index.js";import{formatNodeEngineOverrideWarning}from"#setup/node-engine.js";import{detectPackageManager}from"#setup/package-manager.js";import{runVercel}from"#setup/primitives/run-vercel.js";import{HumanActionRequiredError}from"#setup/human-action.js";import{createPromptCommandOutput,withPhase}from"#setup/cli/index.js";import{runPackageManagerInstall}from"#setup/primitives/pm/run.js";const SLACKBOT_NOT_ATTACHED_ERROR=`Slackbot provisioning did not attach this project. Slack channel was not added.`;function slackbotFailureCopy(e){switch(e.state){case`not-installed`:return{reason:`Slackbot is not connected to a Slack workspace. Slack channel was not added.`,followUp:"Continuing without Slack — the install timed out and was cleaned up; re-run `eve channels add slack` to try again."};case`cleanup-failed`:return{reason:`The abandoned Slack connector could not be removed. Slack channel was not added.`,followUp:`Continuing without Slack — resolve the cleanup warning above before trying again.`};case`connector-lookup-failed`:return{reason:`Existing Slack connectors could not be inspected. Slack channel was not added.`,followUp:"Continuing without Slack — restore Vercel CLI access, then re-run `eve channels add slack`."};case`installation-check-failed`:return{reason:`Slack workspace installation could not be verified. Slack channel was not added.`,followUp:"Continuing without Slack — verify Vercel Connect is reachable, then re-run `eve channels add slack`."};case`existing-not-installed`:return{reason:`The existing Slack connector is not connected to a Slack workspace. Slack channel was not added.`,followUp:`Continuing without Slack — resolve the existing connector warning above before trying again.`};case`detach-failed`:return{reason:`Slackbot provisioning could not replace the existing trigger destination. Slack channel was not added.`,followUp:"Continuing without Slack — run the `vercel connect detach` and `vercel connect attach` commands above."};case`attach-failed`:return{reason:SLACKBOT_NOT_ATTACHED_ERROR,followUp:"Continuing without Slack — finish event delivery with the `vercel connect attach` command above."};case`create-failed`:return{reason:`Slackbot creation failed.`,followUp:"Continuing without Slack — add it later with `eve channels add slack`."}}}function warnOverwrittenFiles(e,t){for(let n of t??[])e.warning(`Overwrote ${n}`)}function warnCompetingNextConfigFiles(e,t){for(let n of t??[])e.warning(`Found competing Next.js config at ${n}; merge any needed settings into next.config.ts and remove it before starting the preview, or Next.js may ignore the generated eve rewrite.`)}function addChannels(l){let u=l.deps??{ensureChannel,deriveSlackConnectorSlug,provisionSlackbot,reconcileSlackUid,detectPackageManager,runPackageManagerInstall,runVercel,detectDeployment};async function scaffoldSlackChannel(e,t,n,r,i,a){let o=!1;if(!t.slackScaffolded){let t=await u.ensureChannel({projectRoot:n,kind:`slack`,slackConnectorUid:a,slackConnectorSlug:r,force:l.force});warnOverwrittenFiles(e,t.filesOverwritten),t.action===`created`||t.action===`overwritten`?e.success(`Scaffolded channel: slack`):e.info(`Channel "slack" already exists. Skipping file creation.`),o=t.action!==`skipped`,i.slackScaffolded=!0}return i.channelsAdded.push(`slack`),o}async function linkProjectForSlackbot(e,t,n,o,s){if(o)throw new HumanActionRequiredError({kind:`vercel-link`,command:`vercel link`,reason:`Slackbot creation needs this directory linked to a Vercel project.`});if(e.message(`Linking this directory to a Vercel project...`),!await u.runVercel([`link`],{cwd:t,signal:s}))throw s?.throwIfAborted(),Error(`Vercel project linking failed. Slackbot creation did not start.`);let c=mergeProjectResolution(n,projectResolutionFromDeployment(await u.detectDeployment(t,{signal:s})));if(!isProjectResolved(c))throw Error(`Vercel project linking failed. Slackbot creation did not start.`);return c}async function addWebChannelToPayload(e,t,n,r,i,a){if(!t.channelSelection.includes(`web`))return;if(t.webScaffolded){i.channelsAdded.push(`web`);return}e.message(`Scaffolding Web Chat channel files...`);let o={projectRoot:n,kind:`web`,packageManager:r,force:l.force,configureVercelServices:l.configureVercelServices??hasVercelProject(t)};l.evePackage!==void 0&&(o.webPackageVersions={evePackage:l.evePackage});let s=await u.ensureChannel(o);if(a?.throwIfAborted(),warnOverwrittenFiles(e,s.filesOverwritten),s.kind===`web`&&s.action!==`skipped`&&s.nodeEngineOverride!==void 0&&e.warning(formatNodeEngineOverrideWarning(s.nodeEngineOverride)),warnCompetingNextConfigFiles(e,`competingNextConfigFiles`in s?s.competingNextConfigFiles:void 0),s.action===`created`||s.action===`overwritten`){e.success(`Scaffolded channel: web`),i.webScaffolded=!0,i.channelsAdded.push(`web`);return}e.info(`Next.js project detected. Skipping Web Chat scaffolding.`)}function assertSlackProjectReady(e){if(l.ensureLinkedProject===void 0){if(!hasVercelProject(e))throw Error(`Slack requires a Vercel project. Re-run and choose to deploy to Vercel to add Slack.`);if(!isProjectResolved(e.project))throw Error(`Expected a linked Vercel project for Slack, but none was resolved.`)}}async function provisionSlackbotWithControls(e,t,n,r){if(r===void 0&&l.prompter.awaitChoice===void 0)return u.provisionSlackbot(e,t,n);let i={};return r!==void 0&&(i.signal=r),l.prompter.awaitChoice!==void 0&&(i.awaitChoice=l.prompter.awaitChoice),u.provisionSlackbot(e,t,n,void 0,i)}async function scaffoldAttachedSlackChannel(e,t,n,r,i,a){if(!await scaffoldSlackChannel(e,t,n,r,i,a.connectorUid)&&!await u.reconcileSlackUid(e,n,a,`slack/${r}`))throw Error(`Slack connector UID update is required before deployment.`)}async function addSlackChannelToPayload(e,t,n,i,a,o){if(!t.channelSelection.includes(`slack`))return;assertSlackProjectReady(t);let s=await u.deriveSlackConnectorSlug(i,t.agentName);if(t.slackbotCreated){if(!t.slackbotAttached)throw Error(SLACKBOT_NOT_ATTACHED_ERROR);if(!t.deploymentPending)return;let n=t.slackConnectorUid;if(n===void 0)throw Error(`Slack connector UID was not resolved. Slack deployment did not start.`);await scaffoldAttachedSlackChannel(e,t,i,s,a,{state:`attached`,connectorUid:n});return}if(n.createSlackbot!==!0){e.info(`Slack channel was not added because Slackbot setup was skipped.`);return}isProjectResolved(a.project)||(a.project=await linkProjectForSlackbot(e,i,a.project,n.headless,o));let c=await provisionSlackbotWithControls(e,i,s,o);if(o?.throwIfAborted(),c.state===`cancelled`)throw new WizardCancelledError;if(c.state!==`attached`){let t=slackbotFailureCopy(c);if(l.slackbotFailure!==`warn-and-continue`)throw Error(t.reason);e.warning(`${t.reason} ${t.followUp}`);return}a.slackbot={connectorUid:c.connectorUid,chatUrl:c.chatUrl,workspaceName:c.workspaceName},await scaffoldAttachedSlackChannel(e,t,i,s,a,c)}async function installChannelDependencies(e,t,n,r,i){if(r.channelsAdded.length!==0){if(await withPhase(e,`Installing channel dependencies (${n} install)...`,()=>u.runPackageManagerInstall(n,t,{onOutput:createPromptCommandOutput(e),signal:i}))){r.dependenciesInstalled=!0;return}e.warning(`Dependency installation failed. The new channels stay unloadable until \`${n} install\` or a deploy succeeds.`)}}async function performAddChannels(e,t,n){n?.throwIfAborted();let r=l.prompter.log,i=requireProjectPath(e),a={channelsAdded:[],webScaffolded:e.webScaffolded,slackScaffolded:e.slackScaffolded,dependenciesInstalled:!1,project:e.project},o=await u.detectPackageManager(i);return await addWebChannelToPayload(r,e,i,o.kind,a,n),await addSlackChannelToPayload(r,e,t,i,a,n),await installChannelDependencies(r,i,o.kind,a,n),a}return{id:`add-channels`,async gather({state:n}){let r=l.headless??!1;if(r&&n.channelSelection.includes(`slack`))throw Error("Slack setup is interactive. Run `eve channels add slack` from an interactive terminal.");if(!n.channelSelection.includes(`slack`)||l.presetCreateSlackbot!==void 0||n.slackbotCreated)return{headless:r,createSlackbot:l.presetCreateSlackbot};try{return{headless:r,createSlackbot:await l.asker.ask(confirm({key:`create-slackbot`,message:`Do you want to create your slackbot?`}))}}catch(t){if(t instanceof SkippedSignal)return{headless:r,createSlackbot:!1};throw t}},async perform({state:e,input:t,signal:n}){try{return await performAddChannels(e,t,n)}catch(e){if(!(e instanceof WizardCancelledError)){let t=e instanceof Error?e.message:String(e),n=t.split(`
`)[0]?.trim()??t;l.prompter.log.error(n)}throw e}},apply(e,t){let n=[...e.channels];for(let e of t.channelsAdded)n.includes(e)||n.push(e);let r={...e,channels:n,webScaffolded:t.webScaffolded,slackScaffolded:t.slackScaffolded,deploymentPending:e.deploymentPending||t.channelsAdded.length>0,deploymentDependenciesInstalled:t.channelsAdded.length>0?t.dependenciesInstalled:e.deploymentDependenciesInstalled,project:mergeProjectResolution(e.project,t.project)};return t.slackbot===void 0?r:{...r,slackbotCreated:!0,slackbotAttached:!0,slackConnectorUid:t.slackbot.connectorUid,slackChatUrl:t.slackbot.chatUrl,slackWorkspaceName:t.slackbot.workspaceName}}}}export{addChannels};