From 917146a7f3110920f4ee671e2e688f1bfdaffe9a Mon Sep 17 00:00:00 2001 From: gilesb Date: Wed, 7 Jan 2026 19:12:46 +0000 Subject: [PATCH] feat: add publish to L2 button in run detail web UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add POST /ui/publish-run/{run_id} endpoint for UI publishing - Add publish form with HTMX to run detail page - Shows publish button for completed runs with outputs - Uses existing L2 /registry/record-run endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- server.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/server.py b/server.py index 1319d4f..7bb8cb0 100644 --- a/server.py +++ b/server.py @@ -1291,6 +1291,42 @@ async def ui_logout(): return response +@app.post("/ui/publish-run/{run_id}", response_class=HTMLResponse) +async def ui_publish_run(run_id: str, request: Request, output_name: str = Form(...)): + """Publish a run to L2 from the web UI.""" + token = request.cookies.get("auth_token") + if not token: + return HTMLResponse('
Not logged in
') + + # Call L2 to publish the run + try: + resp = http_requests.post( + f"{L2_SERVER}/registry/record-run", + json={"run_id": run_id, "output_name": output_name}, + headers={"Authorization": f"Bearer {token}"}, + timeout=10 + ) + if resp.status_code == 400: + error = resp.json().get("detail", "Bad request") + return HTMLResponse(f'
Error: {error}
') + resp.raise_for_status() + result = resp.json() + return HTMLResponse(f''' +
+ Published to L2 as {result["asset"]["name"]} +
+ ''') + except http_requests.exceptions.HTTPError as e: + error_detail = "" + try: + error_detail = e.response.json().get("detail", str(e)) + except Exception: + error_detail = str(e) + return HTMLResponse(f'
Error: {error_detail}
') + except Exception as e: + return HTMLResponse(f'
Error: {e}
') + + @app.get("/ui/runs", response_class=HTMLResponse) async def ui_runs(request: Request): """HTMX partial: list of runs.""" @@ -1557,6 +1593,7 @@ async def ui_detail_page(run_id: str, request: Request): {run.recipe} - {run.run_id[:8]} | Art DAG L1 + @@ -1706,6 +1743,27 @@ async def ui_detail_page(run_id: str, request: Request): html += f'''

Raw JSON

{provenance_json}
+ ''' + + # Add publish section for completed runs + if run.status == "completed" and run.output_hash: + html += f''' +

Publish to L2

+

Register this transformation output on the L2 ActivityPub server.

+
+
+ + +
+ ''' + + html += '''