Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@ottocode/web-ui

Package Overview
Dependencies
Maintainers
1
Versions
138
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install
Potential malware was recently detected in this package.

Affected versions:

0.1.2060.1.2420.1.2430.1.2440.1.249
+38 more

@ottocode/web-ui

Embeddable web UI for ottocode - pre-built static assets

Source
npmnpm
Version
0.1.291
Version published
Weekly downloads
1.9K
140.8%
Maintainers
1
Weekly downloads
 
Created
Source

@ottocode/web-ui

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.

Features

  • 🎯 One-Line Integration - Import and serve with a single function call
  • 📦 Pre-built Assets - No build step required in your project
  • 🚀 Framework Agnostic - Works with Bun, Express, Fastify, Hono, or any HTTP server
  • 🎨 Full Featured - Complete ottocode web interface with all functionality
  • 📱 Responsive - Modern, mobile-friendly UI built with React and Tailwind CSS
  • Fast - Optimized production build with code splitting
  • 🛣️ Smart Routing - Handles SPA routing and direct asset requests automatically

Installation

npm install @ottocode/web-ui
# or
yarn add @ottocode/web-ui
# or
pnpm add @ottocode/web-ui
# or
bun add @ottocode/web-ui

Quick Start

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) in Bun.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:

  • ✅ Automatic SPA routing
  • ✅ Asset path handling (both /ui/assets/* and /assets/*)
  • ✅ Proper MIME types
  • ✅ 404 fallbacks
  • ✅ Real-time SSE streaming

With Custom Routes

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 });
  }
});

With Root Redirect

Automatically redirect / to /ui:

import { serveWebUI } from '@ottocode/web-ui';

Bun.serve({
  port: 3000,
  fetch: serveWebUI({ 
    prefix: '/ui',
    redirectRoot: true  // '/' → '/ui'
  })
});

Different Prefix

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');

Custom Server URL

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.

API Reference

serveWebUI(options?): (req: Request) => Promise<Response | null>

Creates a request handler that serves the web UI.

Options:

OptionTypeDefaultDescription
prefixstring'/ui'URL prefix for the web UI
redirectRootbooleanfalseRedirect / to the prefix
onNotFound(req: Request) => Response | nullnullCustom 404 handler
serverUrlstringAuto-detectedAPI 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 route
  • null if the request should be handled by other routes

Example:

const handler = serveWebUI({
  prefix: '/dashboard',
  redirectRoot: true,
  onNotFound: (req) => new Response('UI not found', { status: 404 })
});

Bun.serve({ port: 3000, fetch: handler });

getWebUIPath(): string

Returns 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(): string

Returns 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(): boolean

Checks 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);
}

Framework Examples

import { serveWebUI } from '@ottocode/web-ui';

Bun.serve({
  port: 3000,
  fetch: serveWebUI({ prefix: '/ui' })
});

Express

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);

Fastify

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 });

Hono

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;

Node.js HTTP

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);

How It Works

The serveWebUI() function handles all the complexity for you:

  • Prefixed Routes (/ui/*): Strips the prefix and serves the requested file
  • Direct Asset Requests (/assets/*, /vite.svg, etc.): Serves assets directly (for when HTML references them)
  • SPA Fallback: Returns index.html for any unmatched routes under the prefix
  • Security: Prevents directory traversal attacks
  • MIME Types: Automatically sets correct Content-Type headers
  • Cross-Runtime: Works in both Bun and Node.js

This pattern solves the common issue where Vite-built apps reference assets like /assets/index-*.js directly, which would 404 without special handling.

Important Notes

Single Page Application (SPA)

The web UI is a React-based SPA with client-side routing. The serveWebUI() handler automatically:

  • Serves static assets from the web UI path
  • Falls back to index.html for client-side routes
  • Handles both prefixed (/ui/assets/*) and direct (/assets/*) asset requests

API Configuration

The web UI expects an API to be available. By default, it will try to connect to:

  • http://localhost:3000/api (in development)
  • Same origin /api (in production)

You'll need to set up your API endpoints to handle ottocode requests. See the ottocode documentation for API implementation details.

CORS

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;
  }
});

Examples

See the examples directory for complete working examples:

  • Bun Server - Minimal example using Bun's HTTP server with serveWebUI()
  • Express Server - Traditional Express.js integration

Development

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:

  • Builds the web app from apps/web using Vite
  • Copies the production build to dist/web-assets
  • Compiles the TypeScript exports
  • Generates type declarations

What's Included

The package includes:

  • ✅ Complete ottocode web interface
  • ✅ React 19 with optimized production build
  • ✅ TailwindCSS for styling
  • ✅ Code syntax highlighting
  • ✅ Markdown rendering
  • ✅ Real-time API communication
  • ✅ Responsive mobile design
  • ✅ Dark mode support (if configured)
  • ✅ Smart request handler with automatic routing

Bundle Size

The production build is optimized and includes:

  • Main JS bundle: ~1.1 MB (370 KB gzipped)
  • CSS: ~31 KB (6.5 KB gzipped)
  • Total initial load: ~376 KB gzipped

Browser Support

The web UI supports all modern browsers:

  • Chrome/Edge (last 2 versions)
  • Firefox (last 2 versions)
  • Safari (last 2 versions)

Migration from Manual Setup

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' })
});

License

MIT

Support

For issues, questions, or contributions:

Keywords

otto

FAQs

Package last updated on 29 May 2026

Did you know?

Socket

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.

Install

Related posts