forked from bots-garden/ori
ori-desktop — a Wails desktop shell for ori
ori-desktop wraps the ori web client in a native window built with
Wails v2 (Go + the platform's webview). It does not reimplement ori:
the ori server keeps serving the React SPA and the /ws, /api/files, /ws/terminal
endpoints. The desktop app only
- shows a small connection screen with the ori server URL (default
http://localhost:8888),
remembered between runs in a JSON settings file; - checks the server with
GET /healthzthrough a Go-bound method; - then displays the ori webapp itself inside the window.
It is a separate Go module (rickub.com/bots-garden/ori-desktop), so the parent
repository's go test ./... and make build are unaffected.
Prerequisites
- Go 1.25+ (the module follows Wails v2.16's requirement).
- The Wails v2 CLI:
go install github.com/wailsapp/wails/v2/cmd/wails@v2.16.0
(then make sure$(go env GOPATH)/binis on yourPATH). - The platform GUI toolchain Wails needs — run
wails doctorto see what is missing:- Linux:
gcc,pkg-config,libgtk-3-dev,libwebkit2gtk-4.0-dev(or-4.1-dev
with-tags webkit2_41); - macOS: Xcode command line tools;
- Windows: WebView2 runtime (preinstalled on Windows 10/11); NSIS only for the installer.
- Linux:
- A running ori server. From the repository root:
make runormake run-mock.
No Node/npm is needed: the frontend is plain HTML/CSS/JS embedded as-is (no bundler).
Run in development mode
cd ori-desktop
wails dev
wails dev builds the Go side, opens the window, and reloads the frontend when a file under
frontend/src/ changes (assetdir in wails.json). It also serves the app at
http://localhost:34115 so you can open it in a normal browser with devtools and still call the
Go methods.
Build a redistributable binary
cd ori-desktop
wails build # → build/bin/ori-desktop (or .app / .exe)
# or, from the repository root:
make desktop
Cross-compiling for Windows from Linux/macOS works without any C toolchain:
wails build -platform windows/amd64 → build/bin/ori-desktop.exe.
Tests
The connection logic is pure Go and tested without any GUI dependency:
cd ori-desktop && go test ./internal/...
# or, from the repository root:
make desktop-test
internal/settings covers load/save round trips, defaults on a first run, invalid files and URL
normalisation; internal/health covers /healthz against an httptest server (healthy,
non-ori, unreachable, cancelled).
How it connects to ori
┌──────────────── ori-desktop (Wails window) ─────────────────┐
│ frontend/src (embedded, wails:// origin) │
│ connection screen ──► window.go.main.App.Connect(url) │
│ │ Go: NormalizeURL → GET /healthz → Save settings
│ ▼ │
│ <iframe src="http://localhost:8888/"> ◄── ori server serves the SPA,
│ SPA opens ws://localhost:8888/ws, /ws/terminal, /api/files as usual
└─────────────────────────────────────────────────────────────┘
Why an iframe? Wails v2 gives no runtime call to navigate the main webview to an external
URL; the window always shows the embedded frontend (runtime.BrowserOpenURL opens the system
browser instead, which is offered as the "Open in browser" fallback). Loading ori in an
<iframe> keeps a thin native bar (URL, Reload, Open in browser, Disconnect) around the
untouched ori SPA. This works because ori sends no X-Frame-Options/CSP frame-ancestors
header, and because the SPA derives its WebSocket URLs from window.location — inside the
iframe that is the ori origin itself, so ori's localhost:* WebSocket origin check passes.
On macOS, build/darwin/Info.plist sets NSAppTransportSecurity/NSAllowsLocalNetworking so
App Transport Security allows the plain-HTTP loopback load.
Go methods bound to the frontend (app.go)
| Method | Purpose |
|---|---|
GetConfig() → settings.Settings |
Remembered settings (defaults on a first run). |
SaveConfig(settings.Settings) |
Normalise the URL and persist. |
CheckHealth(url) → health.Report |
GET <url>/healthz; never rejects — ok, statusCode, detail. |
Connect(url) → string |
Normalise, check health, remember the URL, return the base URL to load. |
OpenInBrowser(url) |
runtime.BrowserOpenURL fallback. |
SettingsPath() → string |
Where the settings file lives (displayed on the screen). |
The frontend calls them as window.go.main.App.<Method>() (Promises). The typed wrappers that
wails dev/wails build generate under frontend/wailsjs/ are gitignored because nothing
imports them; regenerate them with wails generate module if you want the .d.ts files.
Settings file
<user config dir>/ori-desktop/settings.json, i.e. ~/.config/ori-desktop/settings.json on
Linux, ~/Library/Application Support/ori-desktop/settings.json on macOS,
%AppData%\ori-desktop\settings.json on Windows:
{
"serverUrl": "http://localhost:8888"
}
Layout
ori-desktop/
├── main.go Wails bootstrap (window options, embedded assets, bindings)
├── app.go Go methods bound to the frontend
├── wails.json Wails project configuration
├── internal/settings/ settings file (load/save/normalise) + tests
├── internal/health/ GET /healthz probe + tests
├── frontend/src/ index.html, main.css, main.js (embedded ES module, no bundler)
└── build/ Wails build assets (icon, Info.plist, Windows manifest); build/bin is the output
Status / known limitations
- Verified so far: unit tests,
go vet, and a Windows cross-build (wails build -platform windows/amd64) from a Linux sandbox without GUI libraries. The window itself has not yet been
launched (no WebKitGTK in that sandbox) — see the repository's.memory/notes. - ori is loaded over plain HTTP; use it against local or trusted servers only.
- Ori's
--addrother than:8888simply means typing the matching URL on the connection screen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
|