UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

3 lines 12.6 kB
import{createLogger}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{isCodingAgentLaunch}from"#cli/agent-detection.js";import{eveCliBanner}from"#cli/banner.js";import{startCliLiveRow}from"#cli/ui/live-row.js";import{devBootPhase}from"#internal/dev-boot-progress.js";import{Command,CommanderError,InvalidArgumentError}from"#compiled/commander/index.js";import{registerBuildCommand}from"#cli/commands/build.js";import{resolveApplicationRoot}from"#internal/application/paths.js";import{registerProjectCommands}from"#cli/commands/register-project-commands.js";import{resolveDevUiMode,resolveTuiDisplayOptions}from"#cli/dev/ui-options.js";import{parseDevelopmentHeaderOption,resolveDevelopmentUrlTarget}from"#cli/dev/url-target.js";import{LOG_DISPLAY_MODES,parseLogDisplayMode}from"#cli/dev/tui/log-display-mode.js";import{resolveTuiTitle}from"#cli/dev/tui/target.js";import{parseDevelopmentServerUrl}from"#cli/dev/url.js";const devBootLog=createLogger(`dev.boot`);function createDevBootProgressReporter(e){return t=>{switch(t.type){case`phase-started`:e?.update(`Building your agent`,t.phase),devBootLog.debug(t.phase);return;case`phase-finished`:devBootLog.debug(`${t.phase} finished`,{ms:t.elapsedMs});return;case`before-first-paint`:e?.stop();return;default:return t}}}async function loadPrintApplicationInfo(){return(await import(`#cli/commands/info.js`)).printApplicationInfo}async function loadRunDevelopmentTui(){return(await import(`#cli/dev/tui/tui.js`)).runDevelopmentTui}async function loadRunEvalCommand(){return(await import(`#evals/cli/eval.js`)).runEvalCommand}async function loadStartHost(){return(await import(`#internal/nitro/host.js`)).createDevelopmentServer}const loadIsActiveDevelopmentServerForApp=async()=>(await import(`#internal/nitro/host.js`)).isActiveDevelopmentServerForApp;async function loadStartProductionHost(){return(await import(`#internal/nitro/host.js`)).startProductionServer}function shouldPrintCliBootBanner(e){return e.name()===`info`||e.name()===`dev`||e.name()===`init`}async function waitForShutdownSignal(e){await new Promise((t,n)=>{let r=!1,cleanup=()=>{process.off(`SIGINT`,handleSignal),process.off(`SIGTERM`,handleSignal)},handleSignal=()=>{r||(r=!0,cleanup(),e.close().then(t,n))};process.once(`SIGINT`,handleSignal),process.once(`SIGTERM`,handleSignal)})}async function waitForProductionServer(e){await Promise.race([e.wait(),waitForShutdownSignal({close:()=>e.close()})])}function parsePortOption(e){if(!/^-?\d+$/.test(e))throw new InvalidArgumentError(`Expected a numeric port, received "${e}".`);let t=Number(e);if(t<0||t>65535)throw new InvalidArgumentError(`Expected a port between 0 and 65535, received "${e}".`);return t}const DISPLAY_MODES=new Set([`full`,`collapsed`,`auto-collapsed`,`hidden`]),STATS_MODES=new Set([`tokens`,`tokensPerSecond`]);function parseDisplayMode(e){if(!DISPLAY_MODES.has(e))throw new InvalidArgumentError(`Expected one of ${[...DISPLAY_MODES].join(`, `)}, received "${e}".`);return e}function parseStatsMode(e){if(!STATS_MODES.has(e))throw new InvalidArgumentError(`Expected one of ${[...STATS_MODES].join(`, `)}, received "${e}".`);return e}function parseLogsMode(e){let t=parseLogDisplayMode(e);if(t===void 0)throw new InvalidArgumentError(`Expected one of ${LOG_DISPLAY_MODES.join(`, `)}, received "${e}".`);return t}function parseContextSizeOption(e){let t=Number(e);if(!Number.isFinite(t)||t<=0)throw new InvalidArgumentError(`Expected a positive number, received "${e}".`);return t}function hasInteractiveTerminal(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function createCliProgram(e,i){let c=resolveApplicationRoot(),m=resolveInstalledPackageInfo().version,h=new Command,_=createCliTheme();h.name(`eve`).description(`Build and run an eve application.`).version(m).showHelpAfterError().exitOverride().hook(`preAction`,(t,n)=>{shouldPrintCliBootBanner(n)&&e.log(eveCliBanner())}).configureOutput({writeErr:t=>{e.error(t.trimEnd())},writeOut:t=>{e.log(t.trimEnd())}});let v=h.command(`channels`).description(`Manage user-authored channels in the current project.`);v.command(`add [kind]`).description(`Add channels interactively, or scaffold a channel kind (slack | web).`).option(`-f, --force`,`Overwrite existing channel files`).option(`-y, --yes`,`Assume yes for confirmations; requires an explicit channel kind`).action(async(t,n)=>{let{runChannelsAddCommand:r}=await import(`#cli/commands/channels.js`);await r(e,c,{kind:t,options:n})}),v.command(`list`).description(`List user-authored channels in the current project.`).option(`--json`,`Output as JSON`).action(async t=>{let{runChannelsListCommand:n}=await import(`#cli/commands/channels.js`);await n(e,c,t)});let y=h.command(`extension`).description(`Create and build reusable eve extension packages.`);y.command(`init [target]`).description(`Create a new eve extension package.`).option(`-y, --yes`,`Accepted for compatibility; has no effect`).action(async(t,n)=>{n.yes&&e.error(`warning: --yes has no effect for eve extension init.`);let{runExtensionInitCommand:r}=await import(`#cli/commands/extension-init.js`);await r(e,c,t)}),y.command(`build`).description(`Build the current package as an eve extension.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:t}=await import(`#cli/dev/environment.js`);t(c);let{runExtensionBuildCommand:n}=await import(`#cli/commands/extension-build.js`);await n(e,c)}),h.command(`init [target]`).description(`Create a new eve agent, or add one to an existing project directory.`).option(`--channel-web-nextjs`,`Add the Web Chat application (Next.js)`).option(`-y, --yes`,`Accepted for compatibility; has no effect`).action(async(t,n)=>{n.yes&&e.error(`warning: --yes has no effect for eve init.`);let{runInitCommand:r}=await import(`#cli/commands/init.js`);await r(e,c,t,{channelWebNextjs:n.channelWebNextjs})}),registerProjectCommands({program:h,logger:e,appRoot:c}),registerBuildCommand({appRoot:c,buildHost:i.buildHost,logger:e,program:h}),h.command(`start`).description(`Start a built eve application.`).option(`--host <host>`,`Host interface to bind`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).action(async t=>{let{loadDevelopmentEnvironmentFiles:n}=await import(`#cli/dev/environment.js`);n(c);let a=await(i.startProductionHost??await loadStartProductionHost())(c,{host:t.host,port:t.port});e.log(renderCliTaggedLine(_,{message:`server listening at ${a.url}`,tag:`start`,tone:`success`})),await waitForProductionServer(a)}),h.command(`dev`).description(`Start the eve development server or connect to an existing URL.`).argument(`[url]`,`Connect to an existing server URL`,parseDevelopmentServerUrl).option(`--host <host>`,`Host interface to bind`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 2000)`,parsePortOption).option(`-u, --url <url>`,`Connect to an existing server URL`,parseDevelopmentServerUrl).option(`-H, --header <header>`,`Request header for a URL target, in "Name: value" form (repeatable)`,parseDevelopmentHeaderOption).option(`--no-ui`,`Start the server without an interactive UI`).option(`--name <name>`,`Title shown in the terminal UI (defaults to the app folder name)`).option(`--input <text>`,`Pre-fill the prompt input, or start onboarding with /model`).option(`--tools <mode>`,`How tool calls render: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--reasoning <mode>`,`How reasoning renders: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--subagents <mode>`,`How subagent sections render: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--connection-auth <mode>`,`How connection authorization renders: full | collapsed | auto-collapsed | hidden`,parseDisplayMode).option(`--assistant-response-stats <mode>`,`Assistant header statistic: tokens | tokensPerSecond`,parseStatsMode).option(`--context-size <tokens>`,`Model context window size, shown as a usage percentage`,parseContextSizeOption).option(`--logs <mode>`,`Which server/agent logs to show: all | stderr | sandbox | none`,parseLogsMode).addHelpText(`after`,` You can also pass a bare URL, for example: eve dev https://example.com `).action(async(t,n)=>{let a=resolveDevelopmentUrlTarget(n,t),s=a?.serverUrl,u=hasInteractiveTerminal(),d=resolveDevUiMode({options:n,interactive:u});if(n.input!==void 0&&d===`headless`)throw new InvalidArgumentError(`--input requires the interactive UI.`);let f=!1;s!==void 0&&(f=await(i.isActiveDevelopmentServerForApp??await loadIsActiveDevelopmentServerForApp())({appRoot:c,serverUrl:s}));let runInteractiveUi=async(e,t)=>{let r=await devBootPhase(`loading interactive UI`,async()=>i.runDevelopmentTui??await loadRunDevelopmentTui(),t),o=resolveTuiDisplayOptions(n),l=s===void 0||f?{kind:`local`,serverUrl:e.serverUrl,workspaceRoot:e.appRoot??c}:{kind:`remote`,serverUrl:e.serverUrl,workspaceRoot:c},u=resolveTuiTitle({name:n.name,target:l});u!==void 0&&(o.name=u);let d={target:l,initialInput:n.input,onBootProgress:t,...o};a?.headers===void 0?await r(d):await r({...d,headers:a.headers})};if(s){let{loadDevelopmentEnvironmentFiles:t}=await import(`#cli/dev/environment.js`);if(t(c),e.log(`↗ ${f?`local`:`remote`} mode targeting ${_.info(new URL(s).host)}`),d===`headless`){e.log(renderCliTaggedLine(_,{message:`Interactive UI disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}e.log(``),await runInteractiveUi({serverUrl:s});return}d===`tui`&&e.log(``);let p=d===`tui`?startCliLiveRow(e):void 0,m=createDevBootProgressReporter(p);p?.update(`Building your agent`);let h=!1,g,closeServer=async()=>{h||g===void 0||(h=!0,await g.close())};try{g=(i.startHost??await loadStartHost())(c,{existing:d===`tui`?`attach-if-unconfigured`:`reject`,host:n.host,onBootProgress:m,port:n.port});let t=await g.start();if(d!==`tui`&&e.log(renderCliTaggedLine(_,{message:`server listening at ${t.url}`,tag:`dev`,tone:`success`})),d===`headless`)return n.ui!==!1&&!u&&e.log(renderCliTaggedLine(_,{message:`Interactive UI disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await waitForShutdownSignal({close:closeServer});await runInteractiveUi({appRoot:t.appRoot,serverUrl:t.url},m)}finally{p?.stop(),await closeServer()}});let b=h.command(`logs`).description("Inspect local `eve dev` diagnostic logs (.eve/logs).");return b.command(`show [logid]`,{isDefault:!0}).description(`Print a diagnostic log (the most recent when logid is omitted).`).option(`--dump`,`Prepend the log's environment dump (.dump sibling)`).option(`--events`,`Interleave session events from the local workflow store`).action(async(t,n)=>{let{runLogsShowCommand:r}=await import(`#cli/commands/logs.js`);await r(e,c,t,n)}),b.command(`ls`).description(`List diagnostic logs, most recent first.`).option(`--json`,`Output as JSON`).action(async t=>{let{runLogsListCommand:n}=await import(`#cli/commands/logs.js`);await n(e,c,t)}),h.command(`info`).description(`Print resolved application information.`).option(`--json`,`Output as JSON`).action(async t=>{await(i.printApplicationInfo??await loadPrintApplicationInfo())(e,c,t)}),h.command(`eval`).description(`Run evals against an eve agent.`).argument(`[evalIds...]`,`Eval ids (or directory prefixes) to run (all discovered evals when omitted)`).option(`--url <url>`,`Remote agent URL (skip local host startup)`,parseDevelopmentServerUrl).option(`--tag <tag...>`,`Run only evals carrying a tag`).option(`--strict`,`Fail the exit code when any score falls below its threshold`).option(`--list`,`Print discovered evals without running them`).option(`--timeout <ms>`,`Per-eval timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent eval executions`).option(`--json`,`Output results as JSON`).option(`--junit <path>`,`Write JUnit XML results to a file`).option(`--skip-report`,`Skip eval-defined reporters (e.g. Braintrust)`).option(`--verbose`,`Stream per-eval t.log lines to stdout`).action(async(t,n)=>{await(i.runEvalCommand??await loadRunEvalCommand())(t,n,e)}),h}async function runCli(e=process.argv.slice(2),t=console,n={}){let r=createCliProgram(t,n),a=e.length===0?[`dev`]:e;try{await r.parseAsync(a,{from:`user`})}catch(e){if(e instanceof CommanderError){if(e.exitCode===0)return;let r=await(n.isCodingAgentLaunch??isCodingAgentLaunch)();if(a[0]===`init`&&r){let{initAgentInstructions:e}=await import(`#cli/commands/agent-instructions.js`);t.log(e())}throw Error(e.message)}throw e}}export{resolveDevUiMode,resolveTuiDisplayOptions,runCli};