The shim is a shared page, but only windows.ps1 publishes a stage and an
elapsed count. On mac and Linux posix.sh publishes `running` with an empty
message and no clock, so the running branch rendered the h2 back into the
muted line ("Updating Hermes" twice) and started a clock in the browser,
losing "Hermes will open once done." on both platforms.
A clock started in the page measures when the window painted, not how long
the update has been running -- on posix that is the only clock there is, and
it reads zero after the desktop-exit wait has already burned 30s. That is the
hardcoded-milestone problem #75895 removed, in a new costume.
So: elapsed comes from the orchestrator or is not shown. serve-ui.py stamps
it per request from the hand-off start (a value written into the status file
would freeze between publishes, which are minutes apart -- exactly the stall
the line exists to disprove), matching what Windows' in-process listener
already does. posix.sh gets the stages it was missing, at the four gates it
genuinely waits on. Absent a stage the page keeps the settled copy, and an
old orchestrator that sends no clock simply shows no clock.
64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
"""Loopback shim server for the desktop update hand-off.
|
|
|
|
Two GET routes: / serves ui.html, /progress serves the status file the
|
|
orchestrator script writes ({"status": "running"|"done"|"error", ...}).
|
|
Exists because a file:// page cannot receive events from a detached
|
|
process. Prints the chosen ephemeral port on stdout, serves until killed.
|
|
|
|
`elapsed_seconds` is stamped per request, not read from the status file:
|
|
stages are minutes apart, so a value frozen at the last publish would sit
|
|
still through the longest waits -- exactly the stall the page exists to
|
|
disprove. Windows' in-process listener computes it the same way.
|
|
"""
|
|
|
|
import http.server
|
|
import json
|
|
import socketserver
|
|
import sys
|
|
import time
|
|
|
|
html_path, status_path = sys.argv[1], sys.argv[2]
|
|
started_at = float(sys.argv[3]) if len(sys.argv) > 3 else time.time()
|
|
with open(html_path, "rb") as f:
|
|
HTML = f.read()
|
|
|
|
|
|
def progress_body():
|
|
try:
|
|
with open(status_path, "rb") as f:
|
|
state = json.loads(f.read())
|
|
if not isinstance(state, dict):
|
|
raise ValueError(state)
|
|
except Exception:
|
|
state = {"status": "running", "message": ""}
|
|
state["elapsed_seconds"] = max(0, int(time.time() - started_at))
|
|
|
|
return json.dumps(state).encode("utf-8")
|
|
|
|
|
|
class Handler(http.server.BaseHTTPRequestHandler):
|
|
def log_message(self, format, *args): # noqa: A002 - base class signature
|
|
pass
|
|
|
|
def do_GET(self):
|
|
if self.path.startswith("/progress"):
|
|
body = progress_body()
|
|
ctype = "application/json; charset=utf-8"
|
|
elif self.path == "/":
|
|
body, ctype = HTML, "text/html; charset=utf-8"
|
|
else:
|
|
self.send_response(404)
|
|
self.end_headers()
|
|
return
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", ctype)
|
|
self.send_header("Content-Length", str(len(body)))
|
|
self.send_header("Cache-Control", "no-store")
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
|
|
|
|
with socketserver.TCPServer(("127.0.0.1", 0), Handler) as srv:
|
|
print(srv.server_address[1], flush=True)
|
|
srv.serve_forever()
|