
Security News
US Government Forces Anthropic to Pull Claude Fable Days After Launch
Anthropic says the directive cited national security concerns over a narrow jailbreak, but offered no specific technical details.
@ottocode/web-ui
Advanced tools
Pre-built, embeddable web UI for ottocode. This package contains the fully-built static assets from the ottocode web interface, ready to be served by any web server or framework.
npm install @ottocode/web-ui
# or
yarn add @ottocode/web-ui
# or
pnpm add @ottocode/web-ui
# or
bun add @ottocode/web-ui
Just one line - everything is handled for you:
import { serveWebUI } from '@ottocode/web-ui';
Bun.serve({
port: 3000,
idleTimeout: 240, // IMPORTANT: prevents SSE timeout
fetch: serveWebUI({ prefix: '/ui' })
});
console.log('Web UI: http://localhost:3000/ui');
⚠️ Important: Always set
idleTimeout: 240(or higher) inBun.serve()to prevent SSE connection timeouts. The web UI uses Server-Sent Events for real-time streaming, and Bun's default timeout of 10 seconds will cause connections to drop.
That's it! The web UI will be available at /ui with:
/ui/assets/* and /assets/*)Combine the web UI with your own API routes:
import { serveWebUI } from '@ottocode/web-ui';
const webUI = serveWebUI({ prefix: '/ui' });
Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
// Your API routes
if (url.pathname === '/api/hello') {
return new Response(JSON.stringify({ message: 'Hello!' }), {
headers: { 'Content-Type': 'application/json' }
});
}
// Try web UI handler
const webUIResponse = await webUI(req);
if (webUIResponse) return webUIResponse;
// Final fallback
return new Response('Not found', { status: 404 });
}
});
Automatically redirect / to /ui:
import { serveWebUI } from '@ottocode/web-ui';
Bun.serve({
port: 3000,
fetch: serveWebUI({
prefix: '/ui',
redirectRoot: true // '/' → '/ui'
})
});
Serve the UI at any path you want:
import { serveWebUI } from '@ottocode/web-ui';
Bun.serve({
port: 3000,
fetch: serveWebUI({ prefix: '/admin' })
});
console.log('Web UI: http://localhost:3000/admin');
When serving both the API and web UI from the same server, you can configure the web UI to connect to your server instead of the default localhost:9100:
import { createApp } from '@ottocode/server';
import { serveWebUI } from '@ottocode/web-ui';
const port = parseInt(process.env.PORT || '3000', 10);
const host = process.env.HOST || '127.0.0.1';
const app = createApp();
const handleWebUI = serveWebUI({
prefix: '/ui',
serverUrl: `http://${host}:${port}`, // Explicit server URL
});
// Or let it auto-detect (recommended for same-server setup):
// const handleWebUI = serveWebUI({ prefix: '/ui' });
const server = Bun.serve({
port,
hostname: host,
async fetch(req) {
// Serve web UI first
const webUIResponse = await handleWebUI(req);
if (webUIResponse) return webUIResponse;
// Then API routes
return app.fetch(req);
},
});
console.log(`Server: http://${host}:${server.port}/ui`);
Note: If you don't specify
serverUrl, the web UI will automatically detect the server URL from the incoming request. This is recommended when serving both the API and UI from the same server.
serveWebUI(options?): (req: Request) => Promise<Response | null>Creates a request handler that serves the web UI.
Options:
| Option | Type | Default | Description |
|---|---|---|---|
prefix | string | '/ui' | URL prefix for the web UI |
redirectRoot | boolean | false | Redirect / to the prefix |
onNotFound | (req: Request) => Response | null | null | Custom 404 handler |
serverUrl | string | Auto-detected | API server URL for the web UI to connect to. If not provided, auto-detects from request (e.g., http://localhost:3000) |
Returns: A request handler function that returns:
Response if the request matches a web UI routenull if the request should be handled by other routesExample:
const handler = serveWebUI({
prefix: '/dashboard',
redirectRoot: true,
onNotFound: (req) => new Response('UI not found', { status: 404 })
});
Bun.serve({ port: 3000, fetch: handler });
getWebUIPath(): stringReturns the absolute path to the directory containing all built web UI assets.
import { getWebUIPath } from '@ottocode/web-ui';
const assetsPath = getWebUIPath();
// => '/path/to/node_modules/@ottocode/web-ui/dist/web-assets'
getIndexPath(): stringReturns the absolute path to the main index.html file.
import { getIndexPath } from '@ottocode/web-ui';
const indexPath = getIndexPath();
// => '/path/to/node_modules/@ottocode/web-ui/dist/web-assets/index.html'
isWebUIAvailable(): booleanChecks if the web UI assets are properly built and available.
import { isWebUIAvailable } from '@ottocode/web-ui';
if (!isWebUIAvailable()) {
console.error('Web UI assets not found!');
process.exit(1);
}
import { serveWebUI } from '@ottocode/web-ui';
Bun.serve({
port: 3000,
fetch: serveWebUI({ prefix: '/ui' })
});
import express from 'express';
import { getWebUIPath, getIndexPath } from '@ottocode/web-ui';
const app = express();
// Serve static assets
app.use('/ui', express.static(getWebUIPath()));
// SPA fallback
app.get('/ui/*', (req, res) => {
res.sendFile(getIndexPath());
});
app.listen(3000);
import Fastify from 'fastify';
import fastifyStatic from '@fastify/static';
import { getWebUIPath } from '@ottocode/web-ui';
const fastify = Fastify();
await fastify.register(fastifyStatic, {
root: getWebUIPath(),
prefix: '/ui/',
});
await fastify.listen({ port: 3000 });
import { Hono } from 'hono';
import { serveStatic } from 'hono/bun';
import { getWebUIPath } from '@ottocode/web-ui';
const app = new Hono();
app.use('/ui/*', serveStatic({ root: getWebUIPath() }));
export default app;
import { createServer } from 'http';
import { serveWebUI } from '@ottocode/web-ui';
const handler = serveWebUI({ prefix: '/ui' });
createServer(async (req, res) => {
const request = new Request(`http://localhost${req.url}`);
const response = await handler(request);
if (response) {
res.writeHead(response.status, Object.fromEntries(response.headers));
res.end(await response.text());
} else {
res.writeHead(404);
res.end('Not found');
}
}).listen(3000);
The serveWebUI() function handles all the complexity for you:
/ui/*): Strips the prefix and serves the requested file/assets/*, /vite.svg, etc.): Serves assets directly (for when HTML references them)index.html for any unmatched routes under the prefixThis pattern solves the common issue where Vite-built apps reference assets like /assets/index-*.js directly, which would 404 without special handling.
The web UI is a React-based SPA with client-side routing. The serveWebUI() handler automatically:
index.html for client-side routes/ui/assets/*) and direct (/assets/*) asset requestsThe web UI expects an API to be available. By default, it will try to connect to:
http://localhost:3000/api (in development)/api (in production)You'll need to set up your API endpoints to handle ottocode requests. See the ottocode documentation for API implementation details.
If your API is on a different origin than the web UI, you'll need to configure CORS headers:
Bun.serve({
port: 3000,
async fetch(req) {
// Your routes...
const response = await handler(req);
// Add CORS headers
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return response;
}
});
See the examples directory for complete working examples:
serveWebUI()This package is part of the ottocode monorepo. To build from source:
# Clone the repository
git clone https://github.com/yourusername/ottocode
cd ottocode
# Install dependencies
bun install
# Build the package
cd packages/web-ui
bun run build
The build process:
apps/web using Vitedist/web-assetsThe package includes:
The production build is optimized and includes:
The web UI supports all modern browsers:
If you were using the old manual approach:
Before:
import { getWebUIPath, getIndexPath } from '@ottocode/web-ui';
// 50 lines of routing logic...
After:
import { serveWebUI } from '@ottocode/web-ui';
Bun.serve({
port: 3000,
fetch: serveWebUI({ prefix: '/ui' })
});
MIT
For issues, questions, or contributions:
FAQs
Embeddable web UI for ottocode - pre-built static assets
The npm package @ottocode/web-ui receives a total of 2,129 weekly downloads. As such, @ottocode/web-ui popularity was classified as popular.
We found that @ottocode/web-ui demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Anthropic says the directive cited national security concerns over a narrow jailbreak, but offered no specific technical details.

Security News
A network of 152 Chrome live wallpaper extensions hid ad tracking and made extension-driven traffic look like Google search clicks.

Company News
Socket’s first CISO brings deep experience securing high-growth SaaS companies as open source supply chain threats accelerate.