On this page
#Changelog
#0.11.1
Ships an sdist that contains only the package, its tests, its docs and its root metadata.
Context
The source distribution had no file selection, so hatchling's default shipped the entire repository. Every sdist from 0.8.1 through 0.11.0 therefore carried todo/, .rlsbl/, .claude/, .selfdoc/, CI workflows and lockfiles to PyPI -- including twenty screenshots of a private application's interface that were never meant to leave the machine. The screenshots are deleted, and the sdist now uses an allowlist rather than the implicit everything-minus-VCS-ignores default, so a newly added repository directory cannot ship by accident.
Wheels were never affected; they have always been built from src/wesktop.
#Fixes
- sdists no longer include repository housekeeping files. The published source distribution now ships only
src/,tests/,docs/, and the project's root metadata files. Earlier sdists carried the whole repository --todo/,.rlsbl/,.claude/,.selfdoc/, CI workflows and lockfiles -- including image files that were never intended for publication.
#0.11.0
Restore the CLI on strictcli 0.36.0 and pin the floor that keeps it working, and expand the documentation site with a getting-started tutorial and a full SDUI primitive reference
Context
Two threads land together.
The first is the strictcli 0.36.0 migration. 0.36.0 made effect classification mandatory at registration and tightened handler-signature validation, which hard-errored wesktop's command registration -- wesktop --help, wesktop diagnose and any import of wesktop.cli all failed. diagnose is now classified read_only. The code fix alone was not enough: the dependency was declared unpinned, so a fresh install could still resolve a strictcli old enough to reinstate the breakage. The manifest now carries a strictcli>=0.36.0 floor, so an incompatible combination fails to resolve instead of installing and then failing to start.
The second is the documentation site. It gained a step-by-step getting-started tutorial and a reference page for every one of the 40 SDUI primitives, and the API reference was corrected against the shipped code -- surfaces it had drifted away from, or never documented, are now covered. Primitive counts quoted across the site are generated from the source rather than hand-maintained, so they cannot drift again.
This release also adopts the stricttest test-isolation floor. The stance is recorded in pyproject.toml and pinned by tests/test_stricttest_floor.py: off-machine egress is denied, both allowlists are empty, and loopback is allow rather than an exact allowlist because several suites bind real servers on ("127.0.0.1", 0) and the kernel-assigned port cannot be named ahead of time.
#Features
- The documentation site gained a getting-started tutorial and a full SDUI primitive reference. A step-by-step tutorial walks from an empty directory to a packaged desktop app, and every one of the 40 SDUI primitives now has a reference page with its fields and an example. The API reference was also corrected against the shipped code -- surfaces it had drifted away from or never documented are now covered, and the primitive counts quoted across the site are generated from the source instead of hand-maintained.
#Fixes
- The CLI works again on strictcli 0.36.0. strictcli made effect classification mandatory and tightened handler-signature validation, which hard-errored
wesktop's command registration --wesktop --help,wesktop diagnoseand any import ofwesktop.cliall failed.diagnoseis now classifiedread_only. - **
wesktopnow requiresstrictcli>=0.36.0.** The dependency was unpinned, so a fresh install could resolve a strictcli old enough to reject wesktop's command registration -- reinstating thewesktop --help/wesktop diagnosebreakage the same release fixes. Resolution now fails loudly instead of installing a combination that cannot start.
#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.