Skip to content

Commit c018c3f

Browse files
committed
chore(release): bundle webui into wheel and prep 0.2.0
1 parent 0ca0fe2 commit c018c3f

11 files changed

Lines changed: 156 additions & 36 deletions

File tree

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ body:
4949
attributes:
5050
label: nanobot Version
5151
description: Run `nanobot --version` or `pip show nanobot-ai`
52-
placeholder: e.g., 0.1.5
52+
placeholder: e.g., 0.2.0
5353
validations:
5454
required: true
5555

README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,9 @@ nanobot agent
214214
- Want to run nanobot in chat apps like Telegram, Discord, WeChat or Feishu? See [Chat Apps](./docs/chat-apps.md)
215215
- Want Docker or Linux service deployment? See [Deployment](./docs/deployment.md)
216216

217-
## 🧪 WebUI (Development)
217+
## 🌐 WebUI
218218

219-
> [!NOTE]
220-
> The WebUI development workflow currently requires a source checkout and is not yet shipped together with the official packaged release. See [WebUI Document](./webui/README.md) for full WebUI development docs and build steps.
219+
The WebUI ships **inside the published wheel** — no extra build step. Just enable the WebSocket channel and open it in your browser.
221220

222221
<p align="center">
223222
<img src="images/nanobot_webui.png" alt="nanobot webui preview" width="900">
@@ -235,13 +234,12 @@ nanobot agent
235234
nanobot gateway
236235
```
237236

238-
**3. Start the webui dev server**
237+
**3. Open the WebUI**
239238

240-
```bash
241-
cd webui
242-
bun install
243-
bun run dev
244-
```
239+
Visit [`http://127.0.0.1:8765`](http://127.0.0.1:8765) in your browser. To open it from another device on your LAN, see [WebUI docs → LAN access](./webui/README.md#access-from-another-device-lan).
240+
241+
> [!TIP]
242+
> Working on the WebUI itself? Check out [`webui/README.md`](./webui/README.md) for the Vite dev server (HMR) workflow.
245243
246244
## 🏗️ Architecture
247245

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Start here for setup, everyday usage, and deployment.
1515
| Agent social network | [`agent-social-network.md`](./agent-social-network.md) | Join external agent communities from nanobot |
1616
| Configuration | [`configuration.md`](./configuration.md) | Providers, tools, channels, MCP, and runtime settings |
1717
| Image generation | [`image-generation.md`](./image-generation.md) | Configure image providers, WebUI image mode, and generated artifacts |
18+
| WebUI | [`../webui/README.md`](../webui/README.md) | Open the bundled browser UI; LAN access; Vite dev server for contributors |
1819
| Multiple instances | [`multiple-instances.md`](./multiple-instances.md) | Run isolated bots with separate configs and workspaces |
1920
| CLI reference | [`cli-reference.md`](./cli-reference.md) | Core CLI commands and common entrypoints |
2021
| In-chat commands | [`chat-commands.md`](./chat-commands.md) | Slash commands and periodic task behavior |

hatch_build.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""Hatch build hook that bundles the webui (Vite) into nanobot/web/dist.
2+
3+
Triggered automatically by `python -m build` (and any other hatch-driven build)
4+
so published wheels and sdists ship a fresh webui without requiring developers
5+
to remember `cd webui && bun run build` beforehand.
6+
7+
Behaviour:
8+
9+
- Skips for editable installs (`pip install -e .`). Editable mode is for Python
10+
development; webui contributors use `cd webui && bun run dev` (Vite HMR) and
11+
do not need a packaged `dist/`.
12+
- No-op when `webui/package.json` is absent (e.g. installing from an sdist that
13+
already contains a prebuilt `nanobot/web/dist/`).
14+
- Skips when `NANOBOT_SKIP_WEBUI_BUILD=1` is set.
15+
- Skips when `nanobot/web/dist/index.html` already exists, unless
16+
`NANOBOT_FORCE_WEBUI_BUILD=1` is set.
17+
- Uses `bun` when available, otherwise falls back to `npm`. The chosen tool
18+
performs `install` followed by `run build`.
19+
"""
20+
21+
from __future__ import annotations
22+
23+
import os
24+
import shutil
25+
import subprocess
26+
from pathlib import Path
27+
28+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
29+
30+
31+
class WebUIBuildHook(BuildHookInterface):
32+
PLUGIN_NAME = "webui-build"
33+
34+
def initialize(self, version: str, build_data: dict) -> None: # noqa: D401
35+
root = Path(self.root)
36+
webui_dir = root / "webui"
37+
package_json = webui_dir / "package.json"
38+
dist_dir = root / "nanobot" / "web" / "dist"
39+
index_html = dist_dir / "index.html"
40+
41+
# `pip install -e .` builds an editable wheel; skip the (slow) webui
42+
# bundle since editable installs target Python development and webui
43+
# work uses `bun run dev` instead.
44+
if self.target_name == "wheel" and version == "editable":
45+
self.app.display_info(
46+
"[webui-build] skipped for editable install "
47+
"(use `cd webui && bun run build` to bundle webui manually)"
48+
)
49+
return
50+
51+
if os.environ.get("NANOBOT_SKIP_WEBUI_BUILD") == "1":
52+
self.app.display_info("[webui-build] skipped via NANOBOT_SKIP_WEBUI_BUILD=1")
53+
return
54+
55+
if not package_json.is_file():
56+
self.app.display_info(
57+
"[webui-build] no webui/ source tree, assuming prebuilt nanobot/web/dist/"
58+
)
59+
return
60+
61+
force = os.environ.get("NANOBOT_FORCE_WEBUI_BUILD") == "1"
62+
if index_html.is_file() and not force:
63+
self.app.display_info(
64+
f"[webui-build] reusing existing build at {dist_dir} "
65+
"(set NANOBOT_FORCE_WEBUI_BUILD=1 to rebuild)"
66+
)
67+
return
68+
69+
runner = self._pick_runner()
70+
if runner is None:
71+
raise RuntimeError(
72+
"[webui-build] neither `bun` nor `npm` is available on PATH; "
73+
"install one or set NANOBOT_SKIP_WEBUI_BUILD=1 to bypass."
74+
)
75+
76+
self.app.display_info(f"[webui-build] using {runner} to build webui")
77+
self._run([runner, "install"], cwd=webui_dir)
78+
self._run([runner, "run", "build"], cwd=webui_dir)
79+
80+
if not index_html.is_file():
81+
raise RuntimeError(
82+
f"[webui-build] build finished but {index_html} is missing; "
83+
"check webui/vite.config.ts outDir."
84+
)
85+
self.app.display_info(f"[webui-build] webui ready at {dist_dir}")
86+
87+
@staticmethod
88+
def _pick_runner() -> str | None:
89+
for candidate in ("bun", "npm"):
90+
if shutil.which(candidate):
91+
return candidate
92+
return None
93+
94+
def _run(self, cmd: list[str], *, cwd: Path) -> None:
95+
self.app.display_info(f"[webui-build] $ {' '.join(cmd)} (cwd={cwd})")
96+
try:
97+
subprocess.run(cmd, cwd=cwd, check=True)
98+
except subprocess.CalledProcessError as exc:
99+
raise RuntimeError(
100+
f"[webui-build] command failed ({exc.returncode}): {' '.join(cmd)}"
101+
) from exc

nanobot/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def _resolve_version() -> str:
2121
return _pkg_version("nanobot-ai")
2222
except PackageNotFoundError:
2323
# Source checkouts often import nanobot without installed dist-info.
24-
return _read_pyproject_version() or "0.1.5.post3"
24+
return _read_pyproject_version() or "0.2.0"
2525

2626

2727
__version__ = _resolve_version()

nanobot/web/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Embedded web UI assets.
22
3-
The ``dist/`` subdirectory is populated by ``cd webui && bun run build`` and
4-
is shipped in the wheel; it stays empty in source checkouts until that command
5-
has been run.
3+
The ``dist/`` subdirectory holds the production WebUI bundle served by the
4+
gateway. It is shipped inside the published wheel and is rebuilt automatically
5+
by the ``webui-build`` Hatch hook during ``python -m build``. In an editable
6+
source checkout it stays empty until you run ``cd webui && bun run build``
7+
(or use the Vite dev server at ``cd webui && bun run dev``).
68
"""

pyproject.toml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "nanobot-ai"
3-
version = "0.1.5.post3"
3+
version = "0.2.0"
44
description = "A lightweight personal AI assistant framework"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
@@ -121,12 +121,22 @@ build-backend = "hatchling.build"
121121
[tool.hatch.metadata]
122122
allow-direct-references = true
123123

124+
[tool.hatch.build.hooks.custom]
125+
# Implementation lives in the conventional `hatch_build.py` at the repo root.
126+
124127
[tool.hatch.build]
125128
include = [
126129
"nanobot/**/*.py",
127130
"nanobot/templates/**/*.md",
128131
"nanobot/skills/**/*.md",
129132
"nanobot/skills/**/*.sh",
133+
"nanobot/web/dist/**/*",
134+
]
135+
# nanobot/web/dist/ is produced by `cd webui && bun run build` and is
136+
# git-ignored. List it as an artifact so hatch ships it in both wheel and
137+
# sdist even though VCS does not track it.
138+
artifacts = [
139+
"nanobot/web/dist/**/*",
130140
]
131141

132142
[tool.hatch.build.targets.wheel]
@@ -141,7 +151,9 @@ packages = ["nanobot"]
141151
[tool.hatch.build.targets.sdist]
142152
include = [
143153
"nanobot/",
154+
"nanobot/web/dist/",
144155
"bridge/",
156+
"hatch_build.py",
145157
"README.md",
146158
"LICENSE",
147159
"THIRD_PARTY_NOTICES.md",

webui/README.md

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,11 @@ on the same port.
88
For the project overview, install guide, and general docs map, see the root
99
[`README.md`](../README.md).
1010

11-
## Current status
11+
## Just want to use the WebUI?
1212

13-
> [!NOTE]
14-
> The standalone WebUI development workflow currently requires a source
15-
> checkout.
16-
>
17-
> WebUI changes in the GitHub repository may land before they are included in
18-
> the next packaged release, so source installs and published package versions
19-
> are not yet guaranteed to move in lockstep.
13+
If you installed nanobot via `pip install nanobot-ai`, the WebUI is **already bundled** in the wheel. Enable the WebSocket channel in `~/.nanobot/config.json` and run `nanobot gateway` — see the root [`README.md`](../README.md#-webui) for the 3-step setup. You do **not** need anything in this directory.
14+
15+
This `webui/` tree is for people **hacking on the WebUI itself** (UI changes, new components, styling, etc.).
2016

2117
## Layout
2218

@@ -25,7 +21,7 @@ webui/ source tree (this directory)
2521
nanobot/web/dist/ build output served by the gateway
2622
```
2723

28-
## Develop from source
24+
## Develop the WebUI (Vite HMR)
2925

3026
### 1. Install nanobot from source
3127

@@ -35,6 +31,8 @@ From the repository root:
3531
pip install -e .
3632
```
3733

34+
> Editable installs intentionally **skip** the WebUI bundle step — Vite HMR is faster than rebuilding `dist/` on every change.
35+
3836
### 2. Enable the WebSocket channel
3937

4038
In `~/.nanobot/config.json`:
@@ -63,8 +61,7 @@ bun run dev
6361

6462
Then open `http://127.0.0.1:5173`.
6563

66-
By default, the dev server proxies `/api`, `/webui`, `/auth`, and WebSocket
67-
traffic to `http://127.0.0.1:8765`.
64+
By default the dev server proxies `/api`, `/webui`, `/auth`, and WebSocket traffic to `http://127.0.0.1:8765`.
6865

6966
If your gateway listens on a non-default port, point the dev server at it:
7067

@@ -74,7 +71,7 @@ NANOBOT_API_URL=http://127.0.0.1:9000 bun run dev
7471

7572
### Access from another device (LAN)
7673

77-
To use the webui from another device on the same network, set `host` to `"0.0.0.0"` and configure a `token` or `tokenIssueSecret` in `~/.nanobot/config.json`:
74+
To use the WebUI from another device on the same network, set `host` to `"0.0.0.0"` and configure a `token` or `tokenIssueSecret` in `~/.nanobot/config.json`:
7875

7976
```json
8077
{
@@ -91,20 +88,20 @@ To use the webui from another device on the same network, set `host` to `"0.0.0.
9188

9289
The gateway will refuse to start if `host` is `"0.0.0.0"` and neither `token` nor `tokenIssueSecret` is set.
9390

94-
Then open `http://<your-ip>:8765` on the other device. The webui will show an authentication form where you enter the secret. It is saved in your browser so you only need to enter it once.
91+
Then open `http://<your-ip>:8765` on the other device. The WebUI will show an authentication form where you enter the secret. It is saved in your browser so you only need to enter it once.
9592

9693
## Build for packaged runtime
9794

95+
You usually do not need to run this by hand: `python -m build` invokes the WebUI build automatically when packaging the wheel.
96+
97+
If you want to preview the production bundle locally without rebuilding the wheel:
98+
9899
```bash
99100
cd webui
100-
bun run build
101+
bun run build # writes to ../nanobot/web/dist
101102
```
102103

103-
This writes the production assets to `../nanobot/web/dist`, which is the
104-
directory served by `nanobot gateway` and bundled into the Python wheel.
105-
106-
If you are cutting a release, run the build before packaging so the published
107-
wheel contains the current WebUI assets.
104+
The gateway picks up the new bundle on the next restart.
108105

109106
## Test
110107

webui/bun.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webui/src/main.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import React from "react";
21
import ReactDOM from "react-dom/client";
32

43
import App from "./App";

0 commit comments

Comments
 (0)