eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
2 lines • 9.83 kB
JavaScript
import{join}from"node:path";import{mkdir,readFile,rm,writeFile}from"node:fs/promises";import{detectPackageManager}from"#setup/package-manager.js";import{eveDevArguments}from"#setup/primitives/index.js";import{loadDevelopmentEnvironmentFiles}from"#cli/dev/environment.js";import{startDevelopmentSandboxPrewarmInBackground}from"#execution/sandbox/development-prewarm.js";import{devBootPhase}from"#internal/dev-boot-progress.js";import{EVE_DEV_ENV_FLAG}from"#internal/application/optional-package-install.js";import{EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV,clearInitializedDevelopmentSandboxBackendNames,createDevelopmentSandboxRunId,getInitializedDevelopmentSandboxBackendNames}from"#execution/sandbox/development-run.js";import{pruneDevelopmentRuntimeArtifactsSnapshotsInBackground}from"#internal/nitro/dev-runtime-artifacts.js";import{build,createDevServer,prepare}from"nitro/builder";import{createApplicationNitro}from"#internal/nitro/host/create-application-nitro.js";import{prepareApplicationHost}from"#internal/nitro/host/prepare-application-host.js";import{createNitroArtifactsConfig}from"#internal/nitro/host/artifacts-config.js";import{resolveNitroCompiledArtifactsSource}from"#internal/nitro/routes/runtime-artifacts.js";import{pruneLocalSandboxTemplatesInBackground,stopDevelopmentSandboxResources}from"#execution/sandbox/bindings/local.js";import{DEFAULT_DEVELOPMENT_SERVER_PORT,MAX_DEVELOPMENT_SERVER_PORT_ATTEMPTS}from"#internal/nitro/host/ports.js";const MAX_ALLOWED_DEVELOPMENT_SERVER_PORT=65535,WORKFLOW_LOCAL_BASE_URL_ENV=`WORKFLOW_LOCAL_BASE_URL`,PORT_ENV=`PORT`,DEFAULT_DEVELOPMENT_SERVER_HOST=`127.0.0.1`,IPV6_WILDCARD_LISTEN_HOSTNAMES=new Set([`[::]`,`::`]);function normalizeDevelopmentServerClientUrl(e){let t=new URL(e);return IPV6_WILDCARD_LISTEN_HOSTNAMES.has(t.hostname)?(t.hostname=`[::1]`,t.toString()):t.hostname===`0.0.0.0`?(t.hostname=DEFAULT_DEVELOPMENT_SERVER_HOST,t.toString()):e}function isAddressInUseError(e){return e instanceof Error&&`code`in e&&e.code===`EADDRINUSE`}function resolveDevelopmentServerPort(e){let t=typeof e==`string`?Number(e):e??DEFAULT_DEVELOPMENT_SERVER_PORT;if(!Number.isInteger(t)||t<0||t>MAX_ALLOWED_DEVELOPMENT_SERVER_PORT)throw Error(`Invalid development server port "${String(e)}". Expected an integer between 0 and ${MAX_ALLOWED_DEVELOPMENT_SERVER_PORT}.`);return t}function readEnvironmentPort(){let e=process.env[PORT_ENV];if(e===void 0||e.trim()===``)return;let t=Number(e);if(!Number.isInteger(t)||t<0||t>MAX_ALLOWED_DEVELOPMENT_SERVER_PORT)throw Error(`Invalid ${PORT_ENV} environment variable "${e}". Expected an integer between 0 and ${MAX_ALLOWED_DEVELOPMENT_SERVER_PORT}.`);return t}function resolveDevelopmentProcessIdPath(t){return join(t,`.eve`,`dev-process.pid`)}function resolveDevelopmentServerMetadataPath(t){return join(t,`.eve`,`dev-server.json`)}function parseProcessId(e){let t=e.trim();if(!/^\d+$/.test(t))return;let n=Number(t);return Number.isSafeInteger(n)&&n>0?n:void 0}function isProcessRunning(e){try{return process.kill(e,0),!0}catch(e){return e instanceof Error&&`code`in e&&e.code===`EPERM`}}function formatKillCommand(e){return process.platform===`win32`?`taskkill /PID ${e}`:`kill ${e}`}function parseDevelopmentServerMetadata(e){let t;try{t=JSON.parse(e)}catch{return}if(typeof t!=`object`||!t||!(`pid`in t)||typeof t.pid!=`number`||!Number.isSafeInteger(t.pid)||t.pid<=0||!(`url`in t)||typeof t.url!=`string`)return;let n=normalizeDevelopmentServerClientUrl(t.url);try{let e=new URL(n);if(e.protocol!==`http:`&&e.protocol!==`https:`)return}catch{return}return{processId:t.pid,url:n}}async function readDevelopmentServerMetadata(e){try{return parseDevelopmentServerMetadata(await readFile(resolveDevelopmentServerMetadataPath(e),`utf8`))}catch{return}}async function readActiveDevelopmentProcess(e){let t;try{t=parseProcessId(await readFile(resolveDevelopmentProcessIdPath(e),`utf8`))}catch{return}if(t===void 0||!isProcessRunning(t))return;let r=await readDevelopmentServerMetadata(e);return{processId:t,url:r?.processId===t?r.url:void 0}}async function detectDevelopmentCommandPackageManager(e){try{return(await detectPackageManager(e)).kind}catch{return`pnpm`}}async function formatDevelopmentServerConnectCommand(e,t){let n=await detectDevelopmentCommandPackageManager(e);return[n,...eveDevArguments(n),t].join(` `)}async function writeDevelopmentServerMetadata(e,t){await writeFile(resolveDevelopmentServerMetadataPath(e),`${JSON.stringify({pid:process.pid,updatedAt:new Date().toISOString(),url:normalizeDevelopmentServerClientUrl(t)},null,2)}\n`,`utf8`)}async function writeDevelopmentProcessId(a){let o=resolveDevelopmentProcessIdPath(a),s=resolveDevelopmentServerMetadataPath(a),c=await readActiveDevelopmentProcess(a);if(c!==void 0){let e=await formatDevelopmentServerConnectCommand(a,c.url??`http://localhost:PORT`);throw Error([`A dev server is already running for this eve agent (pid ${c.processId}).`,`To connect to the existing instance, run: ${e}`,`To stop it, run: ${formatKillCommand(c.processId)}`].join(`
`))}return await mkdir(join(a,`.eve`),{recursive:!0}),await writeFile(o,`${process.pid}\n`,`utf8`),async()=>{let e;try{e=parseProcessId(await readFile(o,`utf8`))}catch{return}if(e===process.pid){await rm(o,{force:!0}),await rm(s,{force:!0});return}(await readDevelopmentServerMetadata(a))?.processId===process.pid&&await rm(s,{force:!0})}}function resolveDevelopmentServerPorts(e){let t=resolveDevelopmentServerPort(e.port);if(t===0||!e.retryOnAddressInUse)return[t];let n=[];for(let e=0;e<MAX_DEVELOPMENT_SERVER_PORT_ATTEMPTS;e+=1){let r=t+e;if(r>65535)break;n.push(r)}return n}function installWorkflowLocalQueueEnvironment(e){let t=process.env[WORKFLOW_LOCAL_BASE_URL_ENV],n=process.env[PORT_ENV],r=new URL(normalizeDevelopmentServerClientUrl(e));return process.env[WORKFLOW_LOCAL_BASE_URL_ENV]=r.origin,r.port&&(process.env[PORT_ENV]=r.port),()=>{t===void 0?delete process.env[WORKFLOW_LOCAL_BASE_URL_ENV]:process.env[WORKFLOW_LOCAL_BASE_URL_ENV]=t,n===void 0?delete process.env[PORT_ENV]:process.env[PORT_ENV]=n}}function attachTemporarySocketErrorHandler(e){let onSocketError=()=>{};return e.once(`error`,onSocketError),()=>{e.off(`error`,onSocketError)}}function shouldProxyDevelopmentServerWebSocketUpgrades(e){return e.options.features.websocket===!0||e.options.experimental.websocket===!0}function guardDevelopmentServerWebSocketUpgrades(e,t){let n=t.upgrade.bind(t),r=shouldProxyDevelopmentServerWebSocketUpgrades(e);t.upgrade=async(e,t,i)=>{if(!r){t.destroyed||t.destroy();return}let a=attachTemporarySocketErrorHandler(t);try{await n(e,t,i)}catch{t.destroyed||t.destroy()}finally{a()}}}async function listenForDevelopmentServer(e){let t=resolveDevelopmentServerPorts({port:e.port,retryOnAddressInUse:e.retryOnAddressInUse}),n;for(let r of t){let t=e.devServer.listen({hostname:e.host,port:r,silent:!0});try{return await t.ready(),t}catch(r){if(n=r,await t.close().catch(()=>{}),!isAddressInUseError(r)||!e.retryOnAddressInUse)throw r}}throw Error(`Failed to start Nitro dev server after ${t.length} attempts. Tried ports ${t.join(`, `)}.`,{cause:n})}async function startDevelopmentServer(e,t={}){process.env[EVE_DEV_ENV_FLAG]??=`1`,loadDevelopmentEnvironmentFiles(e);let n=process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV],r=createDevelopmentSandboxRunId();process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV]=r;let i,a,o,f,p;try{let s=await devBootPhase(`compiling agent`,()=>prepareApplicationHost(e,{dev:!0}),t.onBootProgress);p=await writeDevelopmentProcessId(s.appRoot),pruneDevelopmentRuntimeArtifactsSnapshotsInBackground(s.appRoot);let l=resolveNitroCompiledArtifactsSource(createNitroArtifactsConfig({appRoot:s.appRoot,dev:!0}));startDevelopmentSandboxPrewarmInBackground({appRoot:s.appRoot,compiledArtifactsSource:l}),pruneLocalSandboxTemplatesInBackground(s.appRoot);let u=await devBootPhase(`creating dev server`,()=>createApplicationNitro(s,!0),t.onBootProgress);i=u,a=createDevServer(u);let d=a;guardDevelopmentServerWebSocketUpgrades(u,a);let m=t.host??u.options.devServer.hostname??DEFAULT_DEVELOPMENT_SERVER_HOST,h=t.port??readEnvironmentPort(),g=h===void 0,_=await devBootPhase(`binding port`,()=>listenForDevelopmentServer({devServer:d,host:m,port:h,retryOnAddressInUse:g}),t.onBootProgress);if(!_.url)throw Error(`Nitro dev server did not expose a URL.`);let v=normalizeDevelopmentServerClientUrl(_.url);await writeDevelopmentServerMetadata(s.appRoot,v),o=installWorkflowLocalQueueEnvironment(v),await devBootPhase(`building dev bundle`,async()=>{await prepare(u),await build(u)},t.onBootProgress),f=await devBootPhase(`starting file watcher`,async()=>{let{startAuthoredSourceWatcher:e}=await import(`#internal/nitro/host/dev-authored-source-watcher.js`);return e({nitro:u,preparedHost:s})},t.onBootProgress);let y=o;if(y===void 0)throw Error(`Workflow local queue environment was not initialized.`);let b=f,x=a,S=u;return{async close(){try{await b.close(),await x.close(),await S.close(),await stopDevelopmentSandboxResources({backendNames:getInitializedDevelopmentSandboxBackendNames(r),devRunId:r,log:e=>console.warn(`[eve:dev] ${e}`)})}finally{clearInitializedDevelopmentSandboxBackendNames(r),await p?.(),y(),restoreDevelopmentSandboxRunId(n)}},url:v}}catch(e){throw await f?.close().catch(()=>{}),o?.(),await a?.close().catch(()=>{}),await i?.close().catch(()=>{}),await stopDevelopmentSandboxResources({backendNames:getInitializedDevelopmentSandboxBackendNames(r),devRunId:r,log:e=>console.warn(`[eve:dev] ${e}`)}).catch(()=>{}),clearInitializedDevelopmentSandboxBackendNames(r),await p?.().catch(()=>{}),restoreDevelopmentSandboxRunId(n),e}}function restoreDevelopmentSandboxRunId(e){if(e===void 0){delete process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV];return}process.env[EVE_DEVELOPMENT_SANDBOX_RUN_ID_ENV]=e}export{normalizeDevelopmentServerClientUrl,startDevelopmentServer};