Installable multi-server client for the Opencode V2 API. Client-side only, no backend. German UI, English code and docs.
Requires Node 20+ and pnpm 9+.
pnpm install
pnpm dev # dev server on http://localhost:5173
pnpm build # typecheck + production build
pnpm preview # preview the production build- Servers are managed under Einstellungen (name, base URL, Basic Auth user/password).
- Stored in
localStorageunder keyopencode-pwa:servers. One user per server. - Every API call sends
Authorization: Basic base64(user:pass)when credentials are set. - Optional:
VITE_DEFAULT_SERVER_URL(see.env.example) pre-selects the matching stored server on first load. - No secrets are committed. Never put credentials in code.
- Manifest and service worker are generated by
vite-plugin-pwa(seevite.config.ts). - Manifest: name "Web PWA for Opencode", icons
pwa-192x192.png,pwa-512x512.png(placeholder 1px PNGs, see AGENTS.todo.md),pwa-icon.svg. - No static
public/manifest.webmanifestis committed: the plugin injects the manifest link at build time. If hosting without plugin output is ever needed, add a static manifest file inpublic/as fallback. registerSW({ immediate: true })insrc/main.tsxenables auto-update.
Via @opencode/client (OpenCode.make({ baseUrl, headers })), see src/lib/opencode.ts:
| Function | Endpoint |
|---|---|
getServerInfo |
GET /api/info (client: server.info()) |
listSessions |
GET /api/session (client: session.list()) |
listMessages |
GET /api/session/:id/message (client: message.list()) |
listAgents |
GET /api/agent (client: agent.list()) |
listProjects |
GET /api/project (client: project.list(), direct-fetch fallback with Basic header) |
listShells |
GET /api/shell (client: shell.list()) |
listPtys |
GET /api/pty (client: pty.list()) |
subscribeEvents |
GET /api/event SSE stream (client: event.subscribe()) |
All calls handle offline/errors gracefully ({ data, error } results, warning banners in UI).
pnpm build # typecheck + production build into dist/-
Upload
dist/to any static host (GitHub Pages, Netlify, nginx). The app needs an SPA fallback toindex.htmlfor client-side routes (/servers/:id, …). -
No secrets in the build: the only build-time input is
VITE_*variables (see.env.example). Server credentials always stay in browserlocalStorage. -
vite.config.tskeeps the defaultbase(nobase: "./"): a Pages sub-path stays a deploy-time concern, not a repo default. -
Browsers call the Opencode servers directly, so each server must allow the PWA origin via CORS. There is no backend or proxy (see
features/01-architecture.md, "Static-Only Invariant"). Start every server with the PWA origin (flag is repeatable, one--corsper additional origin):opencode serve --cors https://ocweb.all-the.rest
Without it the browser blocks every preflight and all API calls fail with "Failed to fetch".
-
Local notifications use the
NotificationAPI only (permission opt-in under Einstellungen → "Benachrichtigungen aktivieren"). No push server, no service-worker push.
index.html
vite.config.ts # tailwind + PWA + dev port 5173 + vitest
playwright.config.ts # chromium + mobile (Pixel 7), webServer pnpm dev
src/
main.tsx # entry, SW registration, ServerProvider
App.tsx # routes
index.css # tailwind + daisyui
components/Layout.tsx # drawer + navbar shell
pages/
Dashboard.tsx # / server list + status
ServerDetail.tsx # /servers/:id sessions + shells + ptys
SessionDetail.tsx # /sessions/:id messages (read-only MVP)
Settings.tsx # /settings server management
lib/opencode.ts # API wrapper
state/servers.tsx # server context + localStorage
lib/*.test.ts(x) # vitest unit tests
tests/e2e/smoke.spec.ts # playwright smoke (@smoke)
features/ # architecture / API contract / userscripts notes
pnpm test # vitest run
pnpm lint # eslint, zero warnings
pnpm lint:fix # eslint --fix
pnpm test:e2e # playwright (starts dev server)
pnpm test:e2e:smoke # only @smoke specs