- Remove unused 'import websockets.exceptions' from main.py (no reference
to websockets.exceptions.* anywhere in the file; all WS handlers use
broad Exception catches)
- Add .gitignore with __pycache__/, *.pyc, .venv/, and other standard
Python excludes so bytecode no longer shows as modified in git status
- Untrack all pre-existing __pycache__/*.pyc files via git rm --cached
Remove unused JSONResponse import from starlette.responses in main.py.
All other stale references (CORSMiddleware, federation_tokens, X-Muxplex-Token,
window.opener, etc.) verified absent. Federation proxy endpoints confirmed present.
Revert index.html to state at 046d123 (task-7 completion), undoing the
out-of-scope settings dialog reorganization from 9f51d1b that:
- Removed the notifications tab button and panel
- Moved #setting-bell-sound into sessions panel
- Moved #setting-device-name into display panel
- Moved #notification-status-text and #notification-request-btn into sessions panel
- Removed #setting-view-scope from devices panel
This restores the 5-tab structure (display, sessions, notifications,
new-session, devices) and correct element placement that the Python
HTML tests assert.
Also remove 2 stale test_frontend_js.py tests that expected buildSources
and _sources to exist after task-1 removed them:
- test_save_remote_instances_rebuilds_sources
- test_build_sources_checks_multi_device_enabled
All 770 Python tests now pass (was 10 failures).
- Delete storeFederationToken function from app.js
- Delete window.addEventListener('message',...) handler from app.js
- Delete buildAuthTileHTML function from app.js
- Delete formatLastSeen function from app.js
- Delete buildOfflineTileHTML function from app.js
- Delete openLoginPopup function from app.js
- Simplify api() to same-origin only (no baseUrl/credentials/X-Muxplex-Token)
- Remove federation auth relay <script> block from index.html
- Remove /api/auth/token endpoint from main.py
- Remove CORSMiddleware from main.py
- Remove X-Muxplex-Token header check from auth.py
- Delete corresponding tests and add absence verification tests
api() returns a Response object; chain .then(res => res.json()) before
accessing data.key so the returned key is displayed in #federation-key-display
and settings-key-display--visible is applied.
Add 4 direct unit tests for buildStatusTileHTML to match coverage
pattern of sibling functions buildAuthTileHTML and buildOfflineTileHTML:
- is exported as a function
- returns article element with correct statusClass
- escapes XSS in deviceName
- renders statusText in badge span
Negative Python list indices bypass the intended 404 guard — e.g.
remote_id=-1 on a non-empty list silently proxied to the last remote
instead of returning 404.
Add an explicit 'idx < 0' check before the list lookup, consistent
with the sibling federation_terminal_ws_proxy endpoint (line 728).
Also adds test_federation_connect_returns_404_for_negative_remote_id
to document the expected behavior and prevent regression.
- Add _log.warning() to bare except clause in fetch_remote so unexpected
errors (JSON parse failures, AttributeError, etc.) leave a diagnostic
trail instead of silently surfacing as 'unreachable'
- Map HTTPStatusError (non-401/403 HTTP errors like 500, 503, 429) to
'unreachable' instead of 'auth_failed' — semantically correct since
these are connectivity/server errors, not authentication failures
- Fix stale task comment in test_api.py: task-8 → task-5
- Use getattr(app.state, 'federation_client', None) in shutdown to guard
against the theoretical case where AsyncClient constructor raised before
setting app.state.federation_client
- Rename 'Shutdown:' comment to 'Cleanup:' in the finally block to better
describe that the block covers both the poll-task cancellation and the
overall cleanup guarantee
Update 9 test_app.mjs tests that regressed when task-17 migrated
from sourceUrl to remoteId throughout app.js:
- buildTileHTML: assert data-remote-id instead of data-source-url
- buildTileHTML data-session-key: assert data-remote-id
- buildSidebarHTML: assert data-remote-id instead of data-source-url
- buildSidebarHTML empty: assert data-remote-id="" not data-source-url=""
- _previewClickHandler: assert remoteId forwarded not sourceUrl
- openSession with remoteId: assert federation proxy URL instead of remote URL
(no credentials: include - same-origin calls don't need it)
- openSession passes remoteId to _openTerminal: assert fed-abc123
- closeSession remote: use remoteId to set _viewingRemoteId state
- cycleViewMode state pollution fixed as side-effect of above fixes
(failing tests no longer leave document.getElementById mocked)
- connectWebSocket() now accepts remoteId instead of sourceUrl; when
remoteId is set the WebSocket URL is ws://host/federation/{remoteId}/terminal/ws
(same-origin, no cross-origin connections)
- openTerminal() signature updated to accept remoteId parameter
- openSession() in app.js routes remote connect POST to
/api/federation/{remoteId}/connect/{name} instead of the remote URL
- window._openTerminal() call passes remoteId instead of sourceUrl
- _viewingSourceUrl state replaced with _viewingRemoteId
- Tile and sidebar click handlers updated to pass remoteId
- HTML attributes changed from data-source-url to data-remote-id
- Test suite updated: removed cross-origin sourceUrl tests, added
federation proxy path tests
test_css_session_grid, test_css_tile_height, and test_css_bell_indicator
each declared a css=None parameter that was immediately overwritten with
read_css(). The parameter was dead API surface — never passed by any caller.
Removed the parameter from all three signatures.
Adds try/except around the outbound http_client.post() call to match the
error-handling pattern established by the federation_sessions sibling endpoint:
- httpx.HTTPStatusError → 502 with upstream status code in detail
- Any other exception (ConnectError, etc.) → 503 with remote URL in detail
Also adds raise_for_status() to surface non-2xx responses explicitly.
Tests added:
- test_federation_connect_returns_503_when_remote_unreachable
- test_federation_connect_returns_502_when_remote_returns_error_status
- Extract shared auth logic into _ws_auth_check(websocket) async helper,
used by both terminal_ws_proxy and federation_terminal_ws_proxy. Eliminates
verbatim duplication of the cookie + bearer auth block.
- Fix URL scheme conversion in federation_terminal_ws_proxy: add elif branch
for http:// so non-http/https URLs fall through to a safe passthrough
(ws_url = remote_url + "/terminal/ws") instead of silently producing a
malformed URL like ws://s://host/terminal/ws.
No behavior change for the supported http/https URL inputs.
All 15 tests pass.
- Add test_instance_info_federation_enabled_true_when_key_exists: writes a
key file to tmp_path, patches FEDERATION_KEY_PATH, and asserts that
federation_enabled is True — completing the positive/negative pair for
task-9's coverage gap (suggested by code quality review).
- Replace 'with TestClient(app) as _:' with 'with TestClient(app):' — the
discard binding was unnecessary (ruff-clean but stylistically noisy).
main.py: Wrapped `app.state.federation_client.aclose()` in a `try/finally` block so the poll task cleanup always runs even if `aclose()` raises unexpectedly.
test_api.py: Extended `test_federation_client_exists_on_app_state` to capture the client reference and assert `client_ref.is_closed` after the `TestClient` context exits, verifying the lifespan shutdown closes the client.
🤖 Generated with Amplifier
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
httpx is imported unconditionally in muxplex/main.py (module-level import),
so it must be a production dependency — not dev-only. Move httpx>=0.27.0
from [project.optional-dependencies].dev to [project.dependencies] to
prevent ModuleNotFoundError on startup in non-dev deployments.