On this page
#Changelog
#0.10.0
Cross-process window lifecycle with per-window marker refcounting (fixes the shared-server kill bug), configurable second-launch behavior, a startup handshake with runtime config injection, and a native runtime bridge.
Context
This release builds on fastware 0.5.0's instance registry and presence-marker API to make multi-process desktop apps behave correctly.
Previously, when two launches shared one background server, closing the last window in one process stopped the server even though another process still had a window open. Window lifecycles are now refcounted across processes via per-window marker files in the fastware instance registry; the server stops only when the last live window (in any process) closes, with dead PIDs pruned by liveness.
wesktop.run(..., second_open=...) makes second-launch behavior an explicit choice: "new-window" (default) opens another window on the running server, or "focus-existing" drops a platform-neutral file-based focus request and exits so the running window is raised -- no DBus, no AppleEvents.
New windows run a startup handshake against /__fastware/version (a loud stderr error if it is unreachable or malformed, with the window staying open) and inject window.__wesktop = {buildId, port, appName} after page load (best-effort, retried once). list_app_instances() enumerates the app's live server and open windows. A host-side native runtime bridge reacts to fastware build changes.
#Features
- Native desktop update bridge. New
wesktop.runtime_bridgemodule lets a desktop host react to fastware build changes:reload(window), best-effortclear_web_cache(window),fetch_build_id(url),check_and_reload(...)(reloads on a DIFFERENT build id), andinstall_focus_poll(...)which polls the version endpoint on window focus where pywebview exposes one.run()now captures the native window handle and wires the bridge. - Desktop runtime wiring.
wesktop.run(..., second_open=...)makes second-launch behavior an explicit choice:"new-window"(default) opens another window on the running server, or"focus-existing"drops a platform-neutral file-based focus request and exits so the running window is raised (no DBus/AppleEvents). New windows also run a startup handshake against/__fastware/version(loud stderr error if unreachable/malformed, window stays open), injectwindow.__wesktop = {buildId, port, appName}after page load (best-effort, retried once), andlist_app_instances()enumerates the app's live server and open windows.
#Fixes
- Shared-server kill bug fixed. When two launches share one background server, closing the last window in one process no longer stops the server while another process still has a window open. Window lifecycles are now refcounted across processes via per-window marker files; the server stops only when the last window (in any process) closes.
#0.9.0
Reference-counted window lifecycle with automatic server shutdown
#Features
- New feature. Server stops automatically when the last native window closes, with reference counting for multi-window scenarios.
#0.8.1
Republish of the 0.8.x line: 0.8.0 never reached the registries due to a CI environment failure
Context
0.8.0 completed its git side (tag and GitHub Release) but the CI-gated publish refused because pytest flaked on GitHub runners: the search_files tests invoke ripgrep, which the CI workflow never installed and whose presence on runners is inconsistent. CI now installs ripgrep before pytest, so the search_files tests pass deterministically. 0.8.0 remains a git-tag-only release with no registry artifacts; 0.8.1 publishes the 0.8.x line to PyPI and npm.
#Fixes
- 0.8.x now installable. The 0.8.0 release never reached PyPI/npm because its publish gate failed on a CI environment flake (missing ripgrep); 0.8.1 publishes the 0.8.x line.
#0.8.0
fastware 0.3.x compatibility and same-origin dev proxy support via backend_prefixes
Context
wesktop 0.7.0 only worked with fastware 0.1.0. This release tracks fastware's 0.3.x hardening and exposes the dev-proxy WebSocket routing via backend_prefixes so downstream apps can run same-origin dev mode. Also includes previously staged work: desktop entry fixes, MCP hardening, and SDUI fixes.
#Features
import wesktopis ~2-5x faster: server/desktop/dev/mcp/sdui symbols now load lazily (granian, mcp, and pydantic are no longer imported eagerly);WebSocketDisconnectis exported from the top level- The
ask_userMCP tool's poll timeout is now configurable. wesktop diagnosenow reports the installed fastware version and whether it imports cleanly.wesktop.dev()now acceptsbackend_prefixes, forwarded to fastware's ViteDevProxy so downstream apps can run same-origin dev mode: the browser loads everything from the Vite origin while/apiand/wsproxy to the backend (no CORS)./wsWebSocket upgrades route to the backend and proxy WebSocket failures close with code 1011.- MCP role API is now documented.
wesktop.ROLES,DEFAULT_ROLE,create_mcp_server, andregister_tools_for_roleappear in the API reference with role-registry and usage guidance.
#Fixes
- Dependency installation no longer requires machine-local checkouts:
uv.lockpreviously carried absolute local paths forstrictcliandfastware, breaking installs and CI outside the author's machine. Dependencies now resolve from PyPI (strictcli 0.24.0, fastware 0.1.0). - wesktop now works with fastware 0.2.x:
TestClientis imported explicitly, the agent role registry (ROLES/DEFAULT_ROLE) is owned by wesktop (passroles=wesktop.ROLEStoregister_tools_for_role/create_mcp_server), and stale private server re-exports are gone - Desktop runtime hardening:
run()detects the GUI backend truthfully per platform with a single probe, registers desktop entries with platform-aware and properly-quotedExeccommands, uses a stable PID path, and hard-errors on unsupportedpre_serve/reloadusage instead of silently misbehaving. - MCP filesystem and git tools harden path handling: the
git_diffpath-traversal guard is fixed and filesystem path handling isos.sep-safe (correct on Windows). - The deployment MCP tool now URL-quotes branch names, so branches containing slashes no longer break deployment requests.
- SDUI serialization now honors pydantic field aliases, and
Stack.to_nodeno longer emits duplicate child nodes. - wesktop now runs against fastware 0.3.1, inheriting its server, auth, WebSocket, routing, and static-file hardening plus a pinned asyncio event loop;
serve()delegates with fastware's new pinnedloop/workersdefaults. - Desktop entry and launcher fixes: Windows shortcut commands parse with an explicit quoting contract (ambiguous unquoted paths hard-error instead of silently breaking), the COM vs PowerShell backend is selected up front by pywin32 availability with no silent fallback,
.desktopExec=escapes%per spec, andremove_entryalso removes the-openlauncher. - Fixed CLI command table in README. The generated README and docs site now show the actual
diagnoseandconfigcommand table instead of a "no strictcli app found" error.
#0.7.0
ASGI framework extracted to fastware. wesktop is now a thin desktop-app layer on top of fastware.
Context
All ASGI, SSE, middleware, server, auth, DI, testing, logging, tasks, features, error_log, audit, config, MCP, and dev modules have been extracted to the fastware package (v0.1.0). wesktop re-exports all symbols for backward compatibility, but the implementation lives in fastware. Four breaking changes: WebSocket binary frames, new server exception types, env var prefix rename, and fastware dependency added.
#Breaking
- Breaking. Extracted ASGI framework modules to fastware package. wesktop now depends on fastware and re-exports all symbols. Import paths (
from wesktop.asgi,from wesktop.server, etc.) continue to work but the underlying implementation is in fastware. Additional breaking changes: WebSocketsend_jsonnow sends binary frames (msgspec) instead of text frames; server errors raisePortInUseError/AlreadyRunningErrorinstead of callingsys.exit(1); default env var prefix changed fromWESKTOP_toFASTWARE_.
#Features
- Expanded CLI help texts for all commands and subcommands.
#0.6.0
#Features
- Feature.
serve_background()runs the server as an independent subprocess that survives the parent process exiting.run()now uses this for full window/server lifecycle separation.
#Fixes
- Fix. Desktop entry launcher scripts no longer hardcode the working directory. Apps resolve paths from their package install location.
#0.5.0
#Breaking
- Remove browser fallback from run(). run() no longer silently falls back to opening a browser when pywebview is unavailable. It now raises RuntimeError, making the failure explicit. Use serve() for headless/browser workflows.
#Features
- Auto-register desktop entry. run() now automatically creates a desktop entry on first launch, removing the need for manual install commands.
- PID resilience and single-instance mode. run() and serve() now handle stale PID files gracefully and support a single_instance parameter to prevent duplicate server instances.
- Multi-window support. A second invocation of run() now opens a new native window connected to the existing server instead of restarting the server.
#Fixes
- Resilient port check. Server startup now probes /health on the target port and can detect and kill stale server processes blocking the port, preventing silent startup failures.
#0.4.5
#Features
- Feature.
ensure_gui_backend()now searches macOS paths (Homebrew and Framework) for system PyGObject.
#Fixes
- Fix.
stop()now handles stale PID files gracefully instead of crashing with ProcessLookupError.
#0.4.4
#Features
- Feature.
ensure_gui_backend()finds and loads system PyGObject in isolated venvs. Called automatically byrun().
#0.4.3
#Fixes
- Fix.
serve(foreground=False)signal handler patch now survives thread start race condition. Previous fix was ineffective because the noop was restored before the daemon thread executed startup.
#0.4.2
#Fixes
- Fix.
run()no longer crashes when pywebview has no GUI backend available (e.g., missing GTK/Qt in uv virtual environments).
#0.4.1
#Fixes
- Fix.
serve(foreground=False)no longer crashes with granian's signal handler registration in daemon threads. Fixes desktop mode (run()) and any background-server usage. - Fix. ViteDevProxy and
dev()now uselocalhostinstead of127.0.0.1, fixing connectivity when Vite binds to IPv6 only. - Fix. ViteDevProxy now tries the backend first for all HTTP requests and only proxies to Vite on 404. Backend routes outside
/api(like/health) are no longer incorrectly proxied.
#0.4.0
#Features
- New feature.
run()accepts ajs_apiparameter, passed through to pywebview'screate_window()for exposing Python methods to JavaScript. - New feature.
dev()starts a Vite dev server alongside the wesktop server in a single command, with ViteDevProxy for unified port access and automatic Vite lifecycle management.
#0.3.2
#Features
- New feature. Forward reload parameter to run().
#Fixes
- Fix. Query list coercion error handling, cached Request.state, typed Receive/Send aliases.
- Fix. Merge lifespan state into WebSocket scope.
- Fix. Replace private config path import with public
config_file_pathproperty. - Fix. Document optional MCP dependency in docstring.
#0.3.1
#Features
- New feature. AppConfig dataclass for centralized app configuration via create_app().
- New feature. serve(reload=True) auto-restarts the server on .py file changes.
- New feature. MCP module with tool registration and routing support.
- New feature. WebSocket.receive_raw() for mixed binary+text frame handling.
- New feature. Broadcaster heartbeat_interval for SSE keepalive.
#Fixes
- Fix. Recursive Pydantic model serialization in WebSocket send results.
- Fix. Corrected module docstring to describe wesktop as a full-featured ASGI framework.
#0.3.0
#Breaking
- Breaking. serve() API redesigned: foreground parameter is required (no default), host/port have no implicit defaults (must be explicit or via env vars).
- Breaking. Error response format changed from {"error": "..."} to {"detail": "..."}. Query parameter type coercion failure now raises 422 instead of silently returning default.
- Breaking. Module-level add_ws_route() removed. Use Router.ws() or Router.add_ws_route() instead (WebSocket routes are now app-scoped).
#Features
- Router. Typed path parameters ({id:int}), greedy {key:path} matching, PUT/PATCH decorators, router composition with include_router (prefix mounting + router-level dependencies), app-scoped WebSocket routing with path params, WebSocket helper class.
- Request/Response. Body reading for all HTTP methods, query_list() for multi-value params, query validation with constraints, cookie extraction, response headers/cookies with set_cookie/delete_cookie helpers, FileResponse, StreamResponse with configurable status, request.method/path/is_disconnected properties, State wrapper.
- Dependency injection. Per-request DI with sync/async factory support, generator cleanup, dependency caching, dependency overrides for testing, router-level dependencies, feature-gated dependencies.
- Auth module. JWT create/verify (HS256), bcrypt password hashing, JSONFileUserStore, get_current_user/require_role DI dependencies, CSRF double-submit middleware, session cookie helpers, decorator-based rate limiting.
- Middleware and observability. structlog integration with auto-detect (JSON/console), RequestID/RequestTiming middleware, CORS and TrustedHost middleware, built-in middleware wiring in create_app, optional Sentry integration, SQLite error log for 5xx responses.
- Pydantic integration. response_model parameter on route decorators for response validation, req.json_as(Model) for request body parsing with 422 error detail.
- Server lifecycle. Unified serve() with stop()/status() utilities, pre_serve callback, app-namespaced env var settings, Vite dev proxy middleware.
- Dev experience. TestClient (sync + async), TOML config loader with optional Pydantic validation.
- Advanced features. Feature flags with JSON overrides, JSONL audit logging, background task registry with feature gating, 39 SDUI UI primitives with provider registry.
#Fixes
- Fix. Pydantic BaseModel auto-serialization in responses and api_prefix-aware SPA fallback.
#0.2.1
#Fixes
- Fix. Use PyPI-published strictcli instead of local source override. Fixes CI test failures.
#0.2.0
#Features
- Lazy JSON. Request.json is now lazily parsed with caching, improving performance for handlers that don't need the body.
- CLI with diagnose command. New
wesktop diagnosecommand checks Python, Node, pywebview, and granian availability. Config file support via strictcli.
#Fixes
- Icon fix. The icon parameter is now correctly wired through to pywebview.
#0.1.1
- No user-facing changes.
#0.1.0
#Features
- Initial release. ASGI router, SSE broadcaster, and extracted server lifecycle.
- Desktop integration. pywebview, desktop entry creation, and public API exports.
- npm shim. Install via
npm i -g @smmh/wesktopfor a globalwesktopCLI that delegates to Python.