Deployment
Build for production
bun run build
bun run previewbun run build compiles all .aihu SFCs through the Rust compiler, bundles with Vite/Rolldown, and validates against the per-package size budgets in .size-limit.json. bun run preview serves the production build locally to verify output before deploying.
App configuration
The app configuration is inline in vite.config.ts, as the argument to viteAihuPlugin({...}) from @aihu/app:
// vite.config.ts
import { viteAihuPlugin } from '@aihu/app'
import { defineConfig } from 'vite'
export default defineConfig({
optimizeDeps: { exclude: ['@aihu/app'] },
plugins: [
viteAihuPlugin({
output: 'static', // 'spa' (default) | 'static' (prerendered)
dir: { pages: 'src/pages' },
build: { bundler: 'vite' }, // 'vite' | 'rolldown'
}),
],
})The config object is @aihu/app's AihuConfig. Key fields: dir (pages / layouts / public / components), output ('spa' | 'static'), site, app.head, css ({ shadowMode: 'light' | 'shadow' }), agentReadiness, adapter, and build.bundler ('vite' | 'rolldown'). Pass the same object to defineConfig from @aihu/app to type-check it in its own file.
A standalone
aihu.config.tsthat default-exportsdefineAihuConfigfrom@aihu/serverstill works as a legacy fallback for server/SSR build config — including abuild.targetof'client','server', or'universal'— but the scaffold no longer emits one, and the inline plugin config is the primary surface.
Cloudflare Workers
Use @aihu/adapter-cloudflare to deploy to Cloudflare Workers or Pages:
// vite.config.ts
import { viteAihuPlugin } from '@aihu/app'
import { cloudflare } from '@aihu/adapter-cloudflare'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
viteAihuPlugin({
adapter: cloudflare({ name: 'my-worker' }),
}),
],
})The adapter:
- Writes
_worker.jsto the Vite output directory (SPA mode — all page requests served from Cloudflare CDN via theASSETSbinding). - Optionally creates
wrangler.tomlin the project root if absent (never overwrites an existing one).
Adapter options:
| Option | Type | Default | Description |
|---|---|---|---|
name |
string |
from package.json |
Cloudflare Worker name in wrangler.toml |
mode |
'workers' | 'pages' |
'workers' |
Deployment target |
generateWrangler |
boolean |
true |
Write wrangler.toml if absent |
Deploy after build:
wrangler deploy --config wrangler.tomlFor a manual Worker without the adapter, use @aihu/server's request router directly:
import { createRequestRouter, defineRoute, json } from '@aihu/server'
const router = createRequestRouter({
routes: [
defineRoute('/api/hello', () => json({ hello: 'world' })),
],
})
// Cloudflare Worker
export default { fetch: router }Vercel
Use @aihu/adapter-vercel to deploy using the Vercel Build Output API v3:
// vite.config.ts
import { viteAihuPlugin } from '@aihu/app'
import { vercel } from '@aihu/adapter-vercel'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
viteAihuPlugin({
adapter: vercel(),
}),
],
})The adapter:
- Copies static assets to
.vercel/output/static/. - Writes an Edge Function entry (default) or Serverless Function entry.
- Emits
config.jsonwith the Build Output API v3 routes manifest.
Adapter options:
| Option | Type | Default | Description |
|---|---|---|---|
runtime |
'edge' | 'serverless' |
'edge' |
Vercel function runtime |
outputDir |
string |
'.vercel/output' |
Build Output API output directory |
nodeVersion |
string |
'nodejs18.x' |
Node.js version for serverless runtime |
Deploy after build:
vercel deploy --prebuiltBun server
Run aihu server-side on Bun using @aihu/server's fetch-API router:
import { createRequestRouter, defineRoute, json } from '@aihu/server'
import { createAgentReadinessRoutes } from '@aihu-plugin/agent-readiness'
const ar = createAgentReadinessRoutes({
name: 'My App',
endpoint: 'https://myapp.example.com/mcp',
summary: 'An aihu-powered app.',
})
const router = createRequestRouter({
routes: [
defineRoute('/llms.txt', ar.llmsTxt),
defineRoute('/.well-known/mcp/server-card.json', ar.mcpServerCard),
defineRoute('/robots.txt', ar.robotsTxt),
defineRoute('/api/hello', () => json({ hello: 'world' })),
],
})
Bun.serve({ fetch: router })Deno
The same router works on Deno Deploy — aihu uses only Web Standard APIs (Fetch, ReadableStream, URL):
import { createRequestRouter, defineRoute, json } from '@aihu/server'
const router = createRequestRouter({
routes: [
defineRoute('/api/hello', () => json({ hello: 'world' })),
],
})
Deno.serve(router)Node.js
aihu output is standard ESM. Any Node.js ≥20.18.0 runtime can serve an aihu application:
npm run build
node dist/server/entry.jsThe server entry is generated by the universal build and uses @aihu/server's request router.
On supported Node platforms @aihu/server lazily loads a native Rust addon to render SSR. Edge runtimes (Cloudflare, Vercel Edge, Deno) automatically skip it and use the TypeScript fallback. To force the fallback on Node — e.g. on an unsupported platform or to debug a parity issue — set SCRIBE_NATIVE_SKIP=1 in the server environment.
viteRouterIntegration() at build time
The Vite plugin performs these steps at build time:
scanPages(dir)— discovers all.aihufiles undersrc/pages/.- For each page, reads the
.route.jsonsidecar emitted by the Rust compiler. - Assembles the route manifest into the
virtual:aihu-routesmodule. - Emits
dist/routes.jsonfor runtime consumption.
Route manifests are fully static after build — no filesystem scanning at runtime.