wesktop v0.10.0 /Changelog
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_bridge module lets a desktop host react to fastware build changes: reload(window), best-effort clear_web_cache(window), fetch_build_id(url), check_and_reload(...) (reloads on a DIFFERENT build id), and install_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), inject window.__wesktop = {buildId, port, appName} after page load (best-effort, retried once), and list_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 wesktop is ~2-5x faster: server/desktop/dev/mcp/sdui symbols now load lazily (granian, mcp, and pydantic are no longer imported eagerly); WebSocketDisconnect is exported from the top level
  • The ask_user MCP tool's poll timeout is now configurable.
  • wesktop diagnose now reports the installed fastware version and whether it imports cleanly.
  • wesktop.dev() now accepts backend_prefixes, forwarded to fastware's ViteDevProxy so downstream apps can run same-origin dev mode: the browser loads everything from the Vite origin while /api and /ws proxy to the backend (no CORS). /ws WebSocket 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, and register_tools_for_role appear in the API reference with role-registry and usage guidance.

#Fixes

  • Dependency installation no longer requires machine-local checkouts: uv.lock previously carried absolute local paths for strictcli and fastware, 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: TestClient is imported explicitly, the agent role registry (ROLES/DEFAULT_ROLE) is owned by wesktop (pass roles=wesktop.ROLES to register_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-quoted Exec commands, uses a stable PID path, and hard-errors on unsupported pre_serve/reload usage instead of silently misbehaving.
  • MCP filesystem and git tools harden path handling: the git_diff path-traversal guard is fixed and filesystem path handling is os.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_node no 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 pinned loop/workers defaults.
  • 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, .desktop Exec= escapes % per spec, and remove_entry also removes the -open launcher.
  • Fixed CLI command table in README. The generated README and docs site now show the actual diagnose and config command 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: WebSocket send_json now sends binary frames (msgspec) instead of text frames; server errors raise PortInUseError/AlreadyRunningError instead of calling sys.exit(1); default env var prefix changed from WESKTOP_ to FASTWARE_.

#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 by run().

#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 use localhost instead of 127.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 a js_api parameter, passed through to pywebview's create_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_path property.
  • 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 diagnose command 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/wesktop for a global wesktop CLI that delegates to Python.