WheelMaker is a self-hosted daemon that lets you use AI coding workflows against your local repositories from a phone or a browser.
Feishu / Mobile App / Web UI β WheelMaker β Claude / Codex / Copilot β your codebase
This README uses the current two-machine shape discussed for this repository:
- Machine A
- runs one hub
- hosts the registry service
- hosts the monitor service
- publishes the Web UI
- exposes a single HTTPS entrypoint through Nginx
- Machine B
- runs another hub
- reports its projects to Machine A through the registry
- Clients
- browsers and phones connect to Machine A over HTTPS / WSS
This model works well when you want one machine to expose the public entrypoint while other machines only contribute projects and agents.
| Location | Endpoint | Purpose |
|---|---|---|
| Machine A / Nginx | https://<host>:28800/ |
Web UI |
| Machine A / Nginx | wss://<host>:28800/ws |
Registry WebSocket |
| Machine A / Nginx | https://<host>:28800/monitor/ |
Monitor UI |
| Machine A / internal | 127.0.0.1:9630 |
Registry listener |
| Machine A / internal | 127.0.0.1:9631 |
Monitor listener |
Requirements:
- Go 1.22+
- Node.js 22+
- an elevated Windows terminal
One-shot refresh:
deploy.batOr run the script directly:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/refresh_server.ps1The refresh flow will:
- pull with
git pull --ff-onlywhen the worktree is clean - install ACP CLI dependencies if needed
- build
wheelmaker.exe,wheelmaker-monitor.exe, andwheelmaker-updater.exe - deploy binaries to
~\.wheelmaker\bin\ - preserve or initialize
~\.wheelmaker\config.json - generate
start.batandstop.bat - register or update these Windows services:
WheelMakerWheelMakerMonitorWheelMakerUpdater
- start the services and enable auto-start
If config.json is created for the first time, the script stops before restart so you can fill it in and run the same command again.
Edit:
notepad ~/.wheelmaker/config.jsonExample for Machine A:
{
"projects": [
{
"name": "WheelMaker",
"path": "D:\\Code\\WheelMaker",
"feishu": {
"app_id": "cli_xxx",
"app_secret": "yyy"
}
}
],
"registry": {
"listen": true,
"port": 9630,
"server": "127.0.0.1",
"token": "replace-with-shared-token",
"hubId": "hub-a"
},
"monitor": {
"port": 9631
},
"log": {
"level": "warn"
}
}Notes:
registry.listen: truemeans Machine A hosts the registry server.registry.portis the internal registry port.registry.tokenis shared by other hubs, Web clients, and the monitor login page.wheelmaker-monitorrefuses to start whenregistry.tokenis empty.registry.hubIdshould be stable and recognizable, for examplehub-a.monitor.portis the internal monitor port that Nginx forwards to.
Machine B does not expose the public entrypoint. It only reports projects to Machine A.
{
"projects": [
{
"name": "Project-B",
"path": "D:\\Code\\Project-B",
"feishu": {
"app_id": "cli_xxx",
"app_secret": "yyy"
}
}
],
"registry": {
"listen": false,
"port": 9630,
"server": "https://machine-a.example.com:28800",
"token": "replace-with-shared-token",
"hubId": "hub-b"
},
"monitor": {
"port": 9631
},
"log": {
"level": "warn"
}
}Notes:
registry.servercan behttps://machine-a.example.com:28800. WheelMaker will convert it towss://.../ws.registry.tokenmust match Machine A and is required by the monitor login page.hubIdmust be unique, for examplehub-b.listen: falsemeans Machine B does not host its own registry listener.
The current local Nginx installation lives under:
D:\Nginx\nginx-1.29.5\
The current routing file is:
D:\Nginx\nginx-1.29.5\conf\conf.d\proxy-28800.conf
A deployment-oriented version of that setup looks like this:
server {
listen 28800 ssl;
server_name _;
ssl_certificate D:/Nginx/cert/stunnel.pem;
ssl_certificate_key D:/Nginx/cert/stunnel.pem;
ssl_trusted_certificate D:/Nginx/cert/uca.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
root C:/Users/<YourUser>/.wheelmaker/web;
index index.html;
try_files $uri $uri/ /index.html;
}
location /ws {
proxy_pass http://127.0.0.1:9630;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
location = /monitor {
return 301 /monitor/;
}
location ^~ /monitor/ {
proxy_pass http://127.0.0.1:9631;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
}
}In this layout:
/serves the published Web UI assets/wsforwards to the registry WebSocket/monitor/forwards to the monitor UI
The Web build is published to:
C:\Users\<YourUser>\.wheelmaker\web
Run:
cd app
npm run build:web:releaseThis will:
- build the Web frontend
- export the assets to
~\.wheelmaker\web - refresh the files served by the Nginx root path
WheelMaker Web already ships with:
manifest.webmanifestservice-worker.jsdisplay: "standalone"
So once the site is served over HTTPS, modern browsers can install it as a local PWA.
- Publish the latest Web build with
npm run build:web:release. - Open
https://<host>:28800/in a supported browser. - Wait until the page finishes loading once so the browser can discover the manifest and register the service worker.
If you open the site through plain HTTP instead of HTTPS, most browsers will not offer PWA install.
- Open
https://<host>:28800/. - Look for the Install app / Install WheelMaker icon in the address bar, or open the browser menu.
- Choose Install.
- The app will be added locally and launch in a standalone window.
- Open
https://<host>:28800/in Chrome or Edge. - Open the browser menu.
- Tap Install app or Add to Home screen.
- Confirm the prompt.
After installation, WheelMaker can be launched from the app drawer or home screen like a native app.
- Open
https://<host>:28800/in Safari. - Tap the Share button.
- Choose Add to Home Screen.
- Confirm the app name and tap Add.
On iOS, the installed app opens from the home screen in a standalone-style window.
- the app opens without normal browser tabs
- the service worker can cache core shell assets
- local notifications and PWA-related capabilities can be enabled by the browser when supported
~/.wheelmaker/start.bat
~/.wheelmaker/stop.bat
~/.wheelmaker/refresh_server.ps1Default refresh flow:
update -> build -> stop -> deploy -> restart
Optional skips:
-SkipUpdate-SkipBuild-SkipStop-SkipDeploy-SkipRestart
You can also trigger the updater manually:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/signal_update_now.ps1 -DelaySeconds 30After deployment:
- Open
https://<host>:28800/and confirm the Web UI loads. - Open
https://<host>:28800/monitor/, enterregistry.token, and confirm the monitor page loads. - Confirm Machine B points
registry.serverat Machine A. - Confirm both machines use the same
registry.token. - Confirm projects from multiple hubs appear in the UI.
- Confirm the browser offers Install app / Add to Home Screen when opened over HTTPS.
| Command | Description |
|---|---|
/use <agent> |
Switch the AI agent (claude, codex, copilot) |
/new |
Start a new session |
/list |
List saved sessions |
/load <id> |
Resume a saved session |
/cancel |
Cancel the current agent operation |
/status |
Show project and agent status |
/mode |
Toggle YOLO mode |
/model |
Switch agent model |
/help |
Show all commands |
The Web app is not only a chat panel. It is a remote workspace that combines project context, sessions, files, Git state, rendering, and connection recovery in one UI.
The app includes full session lifecycle management:
- create new sessions
- resume historical sessions
- switch between session threads
- reload a managed session immediately
- manage sessions across multiple agents
- show session-level config options
The chat view also supports:
- streaming output
- incremental session sync
- full-history hydration
- image attachments
- file link jumps directly from chat content
The app aggregates registry projects into one workspace view:
- multi-project switching
- project online/offline status
- current agent visibility
- project aggregation across multiple hubs
- direct entry into Chat / File / Git from the same shell
File support goes beyond opening a single text blob:
- directory tree browsing
- file open and cache reuse
notModifiedshort-circuit reads- pinned files
- file scroll restoration
- file link navigation
- protection for large files and binary files
The Git view covers the workflows that matter most during remote inspection:
- current branch
- dirty state
- staged / unstaged / untracked summaries
- commit list
- commit file list
- commit diff
- working tree diff
- branch filtering and commit popovers
The chat and code surfaces support rich content rendering:
- Markdown
- tables
- KaTeX math
- Mermaid diagrams
- syntax highlighting
- diff rendering
- theme, font, font size, line height, and tab size settings
The app includes behavior aimed at unstable mobile or backgrounded connections:
- silent reconnect
- keeping the workspace visible during reconnect
- on-demand session and file recovery
- PWA support
- local notifications
- service-worker-backed static asset caching
WheelMaker also includes a configuration and observability surface:
- runtime and registry address settings
- token provider and token stats, including DeepSeek token stats
- monitor page for service status
- log inspection
- hub and registry project visibility
- operational actions such as start / stop / restart / update-publish
WheelMaker/
server/ β Go daemon (hub, agent adapters, registry, monitor)
app/ β React Native mobile app + Web dashboard
docs/ β protocols, design docs, and README visual assets
scripts/ β build, deploy, and update scripts
Server-side:
cd server
go run ./cmd/wheelmaker
go test ./...Web-side:
cd app
npm test -- --runInBand
npm run tsc:web
npm run build:web:releaseScript overview:
scripts\refresh_server.ps1β service-first build and deploymentscripts\signal_update_now.ps1β async updater triggerapp\scripts\export_web_release.ps1β export Web assets to~\.wheelmaker\web
Private β all rights reserved.