Packaging, WebUI build, and cross-platform behavior

Corresponding source

SubjectSource
Python package and build hookspyproject.toml, hatch_build.py
WebUI build configurationwebui/package.json, webui/vite.config.ts
WebUI static-asset integrationnanobot/web/
Platform-sensitive Toolsnanobot/agent/tools/shell.py, sandbox.py
Build and platform teststests/test_package_version.py, tests/tools/test_exec_platform.py, tests/webui/, webui/src/tests/

Learning objectives

After this lesson, you should be able to:

  • explain why the nanobot wheel contains Python, templates, Skills, WebUI, and Bridge content;
  • distinguish the frontend development server from embedded production assets;
  • locate differences between Windows and Unix Shell behavior;
  • choose the correct verification command after changing release-related files.

Prerequisites

  • Know that a wheel is a Python installation package.
  • Know that Vite builds React/TypeScript into browser-loadable static files.
  • Read basic pyproject.toml and package.json fields; React authoring is not required.

1. The product contains more than Python files

The current pyproject.toml builds with Hatchling. The wheel includes:

  • nanobot/**/*.py;
  • prompt templates;
  • built-in Skills;
  • generated nanobot/web/dist;
  • other explicit resources.

force-include puts the repository-root bridge/ directory into the installed package at nanobot/bridge. The final two locations are build/install targets and may not exist in a clean source tree.

Two common changes can therefore work from source and fail after installation:

  • adding a resource without including it in the wheel;
  • changing WebUI source without first generating nanobot/web/dist.

2. Two WebUI runtime modes

Development mode

webui/package.json provides dev. Vite runs on port 5173 by default and proxies HTTP routes such as /webui, /api, and /auth to:

NANOBOT_API_URL

The default is http://127.0.0.1:8765.

The WebSocket client uses its own connection address. Do not assume every WebSocket frame goes through Vite’s HTTP proxy.

Embedded mode

webui/vite.config.ts writes the production build to:

nanobot/web/dist

The Python WebUI server serves static assets from that directory, and the released wheel must include it.

flowchart LR
    A["webui/src<br/>React + TypeScript"] --> B["Vite build"]
    B --> C["nanobot/web/dist"]
    C --> D["Hatch wheel"]
    D --> E["Installed nanobot WebUI"]

    F["bridge/"] -->|"force-include"| D
    G["nanobot/**/*.py"] --> D
    H["templates / Skills"] --> D

3. Bridge boundary

Bridge is not part of AgentLoop. It is an adapter resource for communication between an external platform and nanobot, included so the installed runtime can locate it.

When studying one, first ask whether its message still becomes an InboundMessage, then inspect the Bridge protocol. Shipping Bridge in the wheel is not a reason to put platform logic in the core.

4. Python and frontend version entry points

Key files:

  • pyproject.toml: Python version, dependencies, console script, build includes;
  • webui/package.json: frontend scripts and dependencies;
  • webui/vite.config.ts: development proxy and production output;
  • nanobot/web/: Python static-resource service;
  • nanobot/__init__.py: package version entry point;
  • nanobot/cli/commands.py: CLI entry point.

Use requires-python in pyproject.toml as authority for Python. The surrounding documentation repository requires Node.js 22+ for its website, but nanobot WebUI package management and scripts are governed by webui/package.json and nanobot’s repository documentation.

5. Windows and Unix differences

Shell startup

ExecTool._spawn():

  • uses asyncio.create_subprocess_shell() for a one-line Windows command;
  • explicitly invokes powershell -NoProfile -Command for a multiline Windows command;
  • selects bash by default on Unix, with allowed sh, bash, or zsh;
  • reads the corresponding profile for a Unix login Shell.

Documentation and tests must not assume that Bash quoting works everywhere.

Sandbox

  • bwrap wraps only the non-Windows path;
  • Windows logs a warning and continues unsandboxed;
  • parent-directory fsync may be skipped on Windows when the directory cannot be opened.

Paths and encoding

  • source uses Path.resolve(), os.pathsep, and path normalization extensively;
  • subprocess output is decoded as UTF-8 with replacement for invalid bytes;
  • Session/configuration JSON uses UTF-8 and ensure_ascii=False;
  • on Windows, do not confuse \ path separators with escape sequences.

6. Minimum verification after a change

Python core

From example/nanobot/code-EN:

pytest tests\matching_test_file.py

If the package manifest or resource path changes, build the wheel and inspect its contents rather than checking only the command exit code.

WebUI

From example/nanobot/code-EN/webui, use the project’s lockfile and scripts:

bun run test
bun run build

Then verify that nanobot/web/dist changed and the Python server can serve the entry file.

Repository checks

Project rules explicitly prohibit ruff format because it would rewrite unrelated files. Run only the lint, test, or build command defined by the project and relevant to the change.

This learning library does not modify the source snapshot

These commands are for later isolated practice. Reading the lessons does not require a full build.

7. Common failures

SymptomInspect first
source WebUI works, page is 404 in wheelwhether nanobot/web/dist was built and included
frontend API works, WebSocket connection failsactual WebSocket address, not only the Vite HTTP proxy
command works on Linux but fails on WindowsShell selection, path separator, quoting
bwrap configured but Windows still reaches host resourcesthis is the current explicitly unsupported path
Bridge file missing after installationforce-include in pyproject.toml and wheel contents
non-ASCII path or output is garbledsubprocess encoding, terminal encoding, whether file is UTF-8

8. Source and tests

Configuration and source:

  • pyproject.toml
  • webui/package.json
  • webui/vite.config.ts
  • nanobot/web/
  • nanobot/agent/tools/shell.py
  • nanobot/agent/tools/sandbox.py

Tests and release checks:

  • tests/test_package_version.py
  • tests/cli/test_commands.py
  • tests/tools/test_exec_platform.py
  • tests/webui/
  • webui/src/**/*.test.ts

Concrete test names can change. Locate them from the current output of rg --files tests webui.

Self-check

  1. Why might committing only a webui/src change be insufficient for release?
  2. How do the Vite development proxy and production static assets differ?
  3. What does bwrap actually do on Windows?
  4. After adding a template file, what should you verify beyond Python tests?

Next section: Extension Development