flue-eve

New project

This path starts with a Vite React app and adds the flue-eve Vite plugin. In development, the browser talks to /eve/v1/* on the Vite origin while the Flue server handles the compat routes.

1. Create the app

npm create vite@latest my-app -- --template react-ts
cd my-app

2. Install

npm install flue-eve @flue/runtime hono
npm install -D @flue/cli

flue-eve provides the adapter, @flue/runtime runs the agent, hono hosts the route tree, and @flue/cli provides flue dev.

3. Add the Vite plugin

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { flueEve } from 'flue-eve/vite'

export default defineConfig({
  plugins: [react(), flueEve()],
})

The plugin can scaffold the sidecar that mounts the compat server and can alias Eve imports to flue-eve imports during migration.

4. Initialize the Flue/Eve bridge

Run the CLI from the project root:

npx flue-eve init

It creates the Flue agent file, the Eve compat sidecar, and the app mount when src/app.ts exists. The sidecar is the important piece: it mounts the Eve compat server under /eve/v1.

// src/flue-eve-shim.ts
import { eveCompat, resolveAdmissionFromRuntime } from 'flue-eve/server'
import type { Hono } from 'hono'

export function mountEveCompat(app: Hono): void {
  app.route(
    '/eve/v1',
    eveCompat({
      agentName: 'assistant',
      admission: resolveAdmissionFromRuntime('assistant', {
        flueBaseUrl: process.env.FLUE_BASE_URL,
      }),
    }),
  )
}
// src/app.ts
import { flue } from '@flue/runtime/routing'
import { Hono } from 'hono'
import { mountEveCompat } from './flue-eve-shim.js'

const app = new Hono()

app.route('/', flue())
mountEveCompat(app)

export default app

This is the core bridge: browser requests keep the Eve route shape, while Flue handles agent execution behind the compat server. If you prefer to wire files manually, use the generated code above as the reference shape.

5. Render a chat UI

// src/App.tsx
import { useState } from 'react'
import { useEveAgent } from 'flue-eve/react'

export default function App() {
  const agent = useEveAgent()
  const [message, setMessage] = useState('')
  const busy = agent.status === 'submitted' || agent.status === 'streaming'

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault()
        if (!message.trim() || busy) return
        void agent.send({ message })
        setMessage('')
      }}
    >
      {agent.data.messages.map((item) => (
        <article key={item.id}>
          <strong>{item.role}</strong>
          {item.parts.map((part, index) =>
            part.type === 'text' ? <p key={index}>{part.text}</p> : null,
          )}
        </article>
      ))}

      <input value={message} onChange={(event) => setMessage(event.target.value)} />
      <button disabled={busy || !message.trim()}>Send</button>
      {busy ? <button type="button" onClick={agent.stop}>Stop</button> : null}
    </form>
  )
}

6. Run

npm run dev

Open http://localhost:5173. With mock admission, the agent returns deterministic responses without needing an LLM key.

You can also test the raw route:

curl -X POST http://localhost:5173/eve/v1/session \
  -H 'content-type: application/json' \
  -d '{"message":"Hello"}'

To use real Flue execution, run flue dev and point the compat server at it:

FLUE_BASE_URL=http://127.0.0.1:3583 npm run dev

Next steps