flock-core 0.4.539__py3-none-any.whl → 0.4.541__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

flock/webapp/app/main.py CHANGED
@@ -6,6 +6,7 @@ import shutil
6
6
 
7
7
  # Added for share link creation
8
8
  import uuid
9
+ from datetime import datetime
9
10
  from contextlib import asynccontextmanager
10
11
  from pathlib import Path
11
12
  from typing import Any
@@ -33,6 +34,7 @@ from flock.core.api.run_store import RunStore
33
34
  from flock.core.flock import Flock # For type hinting
34
35
  from flock.core.flock_scheduler import FlockScheduler
35
36
  from flock.core.logging.logging import get_logger # For logging
37
+ from flock.core.logging.live_capture import get_live_log_store
36
38
  from flock.core.util.splitter import parse_schema
37
39
 
38
40
  # Import UI-specific routers
@@ -384,8 +386,7 @@ async def htmx_generate_share_link(
384
386
  ):
385
387
  if not start_agent_name:
386
388
  logger.warning("HTMX generate share link: Agent name not provided.")
387
- return templates.TemplateResponse(
388
- "partials/_share_link_snippet.html",
389
+ return templates.TemplateResponse(request, "partials/_share_link_snippet.html",
389
390
  {"request": request, "error_message": "No agent selected to share."}
390
391
  )
391
392
 
@@ -394,15 +395,13 @@ async def htmx_generate_share_link(
394
395
 
395
396
  if not current_flock_instance or not current_flock_filename:
396
397
  logger.error("HTMX: Cannot create share link: No Flock is currently loaded.")
397
- return templates.TemplateResponse(
398
- "partials/_share_link_snippet.html",
398
+ return templates.TemplateResponse(request, "partials/_share_link_snippet.html",
399
399
  {"request": request, "error_message": "No Flock loaded. Cannot create share link."}
400
400
  )
401
401
 
402
402
  if start_agent_name not in current_flock_instance.agents:
403
403
  logger.error(f"HTMX: Agent '{start_agent_name}' not found in Flock '{current_flock_instance.name}'.")
404
- return templates.TemplateResponse(
405
- "partials/_share_link_snippet.html",
404
+ return templates.TemplateResponse(request, "partials/_share_link_snippet.html",
406
405
  {"request": request, "error_message": f"Agent '{start_agent_name}' not found in current Flock."}
407
406
  )
408
407
 
@@ -415,8 +414,7 @@ async def htmx_generate_share_link(
415
414
  flock_definition_str = flock_file_path.read_text()
416
415
  except Exception as e:
417
416
  logger.error(f"HTMX: Failed to get flock definition for sharing: {e}", exc_info=True)
418
- return templates.TemplateResponse(
419
- "partials/_share_link_snippet.html",
417
+ return templates.TemplateResponse(request, "partials/_share_link_snippet.html",
420
418
  {"request": request, "error_message": "Could not retrieve Flock definition for sharing."}
421
419
  )
422
420
 
@@ -433,14 +431,12 @@ async def htmx_generate_share_link(
433
431
  full_share_url = f"{base_url.rstrip('/')}/ui/shared-run/{share_id}"
434
432
 
435
433
  logger.info(f"HTMX: Generated share link for agent '{start_agent_name}' in Flock '{current_flock_instance.name}' with ID '{share_id}'. URL: {full_share_url}")
436
- return templates.TemplateResponse(
437
- "partials/_share_link_snippet.html",
434
+ return templates.TemplateResponse(request, "partials/_share_link_snippet.html",
438
435
  {"request": request, "share_url": full_share_url, "flock_name": current_flock_instance.name, "agent_name": start_agent_name}
439
436
  )
440
437
  except Exception as e:
441
438
  logger.error(f"HTMX: Failed to create share link for agent '{start_agent_name}': {e}", exc_info=True)
442
- return templates.TemplateResponse(
443
- "partials/_share_link_snippet.html",
439
+ return templates.TemplateResponse(request, "partials/_share_link_snippet.html",
444
440
  {"request": request, "error_message": f"Could not generate link: {e!s}"}
445
441
  )
446
442
  # --- End HTMX Endpoint ---
@@ -457,8 +453,7 @@ async def htmx_generate_share_chat_link(
457
453
  ):
458
454
  if not agent_name:
459
455
  logger.warning("HTMX generate share chat link: Agent name not provided.")
460
- return templates.TemplateResponse(
461
- "partials/_share_chat_link_snippet.html", # Will create this template
456
+ return templates.TemplateResponse(request, "partials/_share_chat_link_snippet.html", # Will create this template
462
457
  {"request": request, "error_message": "No agent selected for chat sharing."}
463
458
  )
464
459
 
@@ -467,15 +462,13 @@ async def htmx_generate_share_chat_link(
467
462
 
468
463
  if not current_flock_instance or not current_flock_filename:
469
464
  logger.error("HTMX Chat Share: Cannot create share link: No Flock is currently loaded.")
470
- return templates.TemplateResponse(
471
- "partials/_share_chat_link_snippet.html",
465
+ return templates.TemplateResponse(request, "partials/_share_chat_link_snippet.html",
472
466
  {"request": request, "error_message": "No Flock loaded. Cannot create share link."}
473
467
  )
474
468
 
475
469
  if agent_name not in current_flock_instance.agents:
476
470
  logger.error(f"HTMX Chat Share: Agent '{agent_name}' not found in Flock '{current_flock_instance.name}'.")
477
- return templates.TemplateResponse(
478
- "partials/_share_chat_link_snippet.html",
471
+ return templates.TemplateResponse(request, "partials/_share_chat_link_snippet.html",
479
472
  {"request": request, "error_message": f"Agent '{agent_name}' not found in current Flock."}
480
473
  )
481
474
 
@@ -488,8 +481,7 @@ async def htmx_generate_share_chat_link(
488
481
  flock_definition_str = flock_file_path.read_text()
489
482
  except Exception as e:
490
483
  logger.error(f"HTMX Chat Share: Failed to get flock definition for sharing: {e}", exc_info=True)
491
- return templates.TemplateResponse(
492
- "partials/_share_chat_link_snippet.html",
484
+ return templates.TemplateResponse(request, "partials/_share_chat_link_snippet.html",
493
485
  {"request": request, "error_message": "Could not retrieve Flock definition for sharing."}
494
486
  )
495
487
 
@@ -517,14 +509,12 @@ async def htmx_generate_share_chat_link(
517
509
  full_share_url = f"{base_url.rstrip('/')}/chat/shared/{share_id}"
518
510
 
519
511
  logger.info(f"HTMX: Generated share CHAT link for agent '{agent_name}' in Flock '{current_flock_instance.name}' with ID '{share_id}'. URL: {full_share_url}")
520
- return templates.TemplateResponse(
521
- "partials/_share_chat_link_snippet.html", # Will create this template
512
+ return templates.TemplateResponse(request, "partials/_share_chat_link_snippet.html", # Will create this template
522
513
  {"request": request, "share_url": full_share_url, "flock_name": current_flock_instance.name, "agent_name": agent_name}
523
514
  )
524
515
  except Exception as e:
525
516
  logger.error(f"HTMX Chat Share: Failed to create share link for agent '{agent_name}': {e}", exc_info=True)
526
- return templates.TemplateResponse(
527
- "partials/_share_chat_link_snippet.html",
517
+ return templates.TemplateResponse(request, "partials/_share_chat_link_snippet.html",
528
518
  {"request": request, "error_message": f"Could not generate chat link: {e!s}"}
529
519
  )
530
520
 
@@ -540,8 +530,7 @@ async def page_shared_run(
540
530
 
541
531
  if not shared_config:
542
532
  logger.warning(f"Share ID {share_id} not found.")
543
- return templates.TemplateResponse(
544
- "error_page.html",
533
+ return templates.TemplateResponse(request, "error_page.html",
545
534
  {"request": request, "error_title": "Link Not Found", "error_message": "The shared link does not exist or may have expired."},
546
535
  status_code=404
547
536
  )
@@ -608,7 +597,7 @@ async def page_shared_run(
608
597
  context["active_theme_name"] = DEFAULT_THEME_NAME
609
598
 
610
599
  # The shared_run_page.html will now be a simple wrapper that includes _execution_form.html
611
- return templates.TemplateResponse("shared_run_page.html", context)
600
+ return templates.TemplateResponse(request, "shared_run_page.html", context)
612
601
 
613
602
  # --- End Route for Shared Run Page ---
614
603
 
@@ -739,7 +728,7 @@ async def page_dashboard(
739
728
  context["initial_content_url"] = str(request.url_for("htmx_get_execution_view_container")) if flock_in_state else str(request.url_for("htmx_scoped_no_flock_view"))
740
729
  else:
741
730
  context["initial_content_url"] = str(request.url_for("htmx_get_load_flock_view"))
742
- return templates.TemplateResponse("base.html", context)
731
+ return templates.TemplateResponse(request, "base.html", context)
743
732
 
744
733
  @app.get("/ui/editor/{section:path}", response_class=HTMLResponse, tags=["UI Pages"])
745
734
  async def page_editor_section(
@@ -763,14 +752,14 @@ async def page_editor_section(
763
752
  }
764
753
  context["initial_content_url"] = content_map.get(section, f"{root_path}/ui/htmx/load-flock-view")
765
754
  if section not in content_map: context["error_message"] = "Invalid editor section."
766
- return templates.TemplateResponse("base.html", context)
755
+ return templates.TemplateResponse(request, "base.html", context)
767
756
 
768
757
  @app.get("/ui/registry", response_class=HTMLResponse, tags=["UI Pages"])
769
758
  async def page_registry(request: Request, error: str = None, success: str = None, ui_mode: str = Query("standalone")):
770
759
  context = get_base_context_web(request, error, success, ui_mode)
771
760
  root_path = request.scope.get("root_path", "")
772
761
  context["initial_content_url"] = f"{root_path}/ui/htmx/registry-viewer"
773
- return templates.TemplateResponse("base.html", context)
762
+ return templates.TemplateResponse(request, "base.html", context)
774
763
 
775
764
  @app.get("/ui/create", response_class=HTMLResponse, tags=["UI Pages"])
776
765
  async def page_create(request: Request, error: str = None, success: str = None, ui_mode: str = Query("standalone")):
@@ -778,23 +767,23 @@ async def page_create(request: Request, error: str = None, success: str = None,
778
767
  context = get_base_context_web(request, error, success, "standalone")
779
768
  root_path = request.scope.get("root_path", "")
780
769
  context["initial_content_url"] = f"{root_path}/ui/htmx/create-flock-form"
781
- return templates.TemplateResponse("base.html", context)
770
+ return templates.TemplateResponse(request, "base.html", context)
782
771
 
783
772
  @app.get("/ui/htmx/sidebar", response_class=HTMLResponse, tags=["UI HTMX Partials"])
784
773
  async def htmx_get_sidebar(request: Request, ui_mode: str = Query("standalone")):
785
- return templates.TemplateResponse("partials/_sidebar.html", get_base_context_web(request, ui_mode=ui_mode))
774
+ return templates.TemplateResponse(request, "partials/_sidebar.html", get_base_context_web(request, ui_mode=ui_mode))
786
775
 
787
776
  @app.get("/ui/htmx/header-flock-status", response_class=HTMLResponse, tags=["UI HTMX Partials"])
788
777
  async def htmx_get_header_flock_status(request: Request, ui_mode: str = Query("standalone")):
789
- return templates.TemplateResponse("partials/_header_flock_status.html", get_base_context_web(request, ui_mode=ui_mode))
778
+ return templates.TemplateResponse(request, "partials/_header_flock_status.html", get_base_context_web(request, ui_mode=ui_mode))
790
779
 
791
780
  @app.get("/ui/htmx/load-flock-view", response_class=HTMLResponse, tags=["UI HTMX Partials"])
792
781
  async def htmx_get_load_flock_view(request: Request, error: str = None, success: str = None, ui_mode: str = Query("standalone")):
793
- return templates.TemplateResponse("partials/_load_manager_view.html", get_base_context_web(request, error, success, ui_mode))
782
+ return templates.TemplateResponse(request, "partials/_load_manager_view.html", get_base_context_web(request, error, success, ui_mode))
794
783
 
795
784
  @app.get("/ui/htmx/dashboard-flock-file-list", response_class=HTMLResponse, tags=["UI HTMX Partials"])
796
785
  async def htmx_get_dashboard_flock_file_list_partial(request: Request):
797
- return templates.TemplateResponse("partials/_dashboard_flock_file_list.html", {"request": request, "flock_files": get_available_flock_files()})
786
+ return templates.TemplateResponse(request, "partials/_dashboard_flock_file_list.html", {"request": request, "flock_files": get_available_flock_files()})
798
787
 
799
788
  @app.get("/ui/htmx/dashboard-default-action-pane", response_class=HTMLResponse, tags=["UI HTMX Partials"])
800
789
  async def htmx_get_dashboard_default_action_pane(request: Request):
@@ -803,11 +792,11 @@ async def htmx_get_dashboard_default_action_pane(request: Request):
803
792
  @app.get("/ui/htmx/dashboard-flock-properties-preview/{filename}", response_class=HTMLResponse, tags=["UI HTMX Partials"])
804
793
  async def htmx_get_dashboard_flock_properties_preview(request: Request, filename: str):
805
794
  preview_flock_data = get_flock_preview_service(filename)
806
- return templates.TemplateResponse("partials/_dashboard_flock_properties_preview.html", {"request": request, "selected_filename": filename, "preview_flock": preview_flock_data})
795
+ return templates.TemplateResponse(request, "partials/_dashboard_flock_properties_preview.html", {"request": request, "selected_filename": filename, "preview_flock": preview_flock_data})
807
796
 
808
797
  @app.get("/ui/htmx/create-flock-form", response_class=HTMLResponse, tags=["UI HTMX Partials"])
809
798
  async def htmx_get_create_flock_form(request: Request, error: str = None, success: str = None, ui_mode: str = Query("standalone")):
810
- return templates.TemplateResponse("partials/_create_flock_form.html", get_base_context_web(request, error, success, ui_mode))
799
+ return templates.TemplateResponse(request, "partials/_create_flock_form.html", get_base_context_web(request, error, success, ui_mode))
811
800
 
812
801
  @app.get("/ui/htmx/agent-manager-view", response_class=HTMLResponse, tags=["UI HTMX Partials"])
813
802
  async def htmx_get_agent_manager_view(request: Request):
@@ -815,20 +804,19 @@ async def htmx_get_agent_manager_view(request: Request):
815
804
  if not context.get("current_flock"): # Check if flock exists in the context
816
805
  return HTMLResponse("<article class='error'><p>No flock loaded. Cannot manage agents.</p></article>")
817
806
  # Pass the 'current_flock' from the context to the template as 'flock'
818
- return templates.TemplateResponse(
819
- "partials/_agent_manager_view.html",
807
+ return templates.TemplateResponse(request, "partials/_agent_manager_view.html",
820
808
  {"request": request, "flock": context.get("current_flock")}
821
809
  )
822
810
 
823
811
  @app.get("/ui/htmx/registry-viewer", response_class=HTMLResponse, tags=["UI HTMX Partials"])
824
812
  async def htmx_get_registry_viewer(request: Request):
825
- return templates.TemplateResponse("partials/_registry_viewer_content.html", get_base_context_web(request))
813
+ return templates.TemplateResponse(request, "partials/_registry_viewer_content.html", get_base_context_web(request))
826
814
 
827
815
  @app.get("/ui/htmx/execution-view-container", response_class=HTMLResponse, tags=["UI HTMX Partials"])
828
816
  async def htmx_get_execution_view_container(request: Request):
829
817
  context = get_base_context_web(request)
830
818
  if not context.get("current_flock"): return HTMLResponse("<article class='error'><p>No Flock loaded. Cannot execute.</p></article>")
831
- return templates.TemplateResponse("partials/_execution_view_container.html", context)
819
+ return templates.TemplateResponse(request, "partials/_execution_view_container.html", context)
832
820
 
833
821
  @app.get("/ui/htmx/scoped-no-flock-view", response_class=HTMLResponse, tags=["UI HTMX Partials"])
834
822
  async def htmx_scoped_no_flock_view(request: Request):
@@ -845,13 +833,13 @@ async def ui_load_flock_by_name_action(request: Request, selected_flock_filename
845
833
  response_headers["HX-Push-Url"] = "/ui/editor/execute?ui_mode=" + ui_mode_query
846
834
  response_headers["HX-Trigger"] = json.dumps({"flockLoaded": None, "notify": {"type": "success", "message": success_message_text}})
847
835
  context = get_base_context_web(request, success=success_message_text, ui_mode=ui_mode_query)
848
- return templates.TemplateResponse("partials/_execution_view_container.html", context, headers=response_headers)
836
+ return templates.TemplateResponse(request, "partials/_execution_view_container.html", context, headers=response_headers)
849
837
  else:
850
838
  error_message_text = f"Failed to load flock file '{selected_flock_filename}'."
851
839
  response_headers["HX-Trigger"] = json.dumps({"notify": {"type": "error", "message": error_message_text}})
852
840
  context = get_base_context_web(request, error=error_message_text, ui_mode=ui_mode_query)
853
841
  context["error_message_inline"] = error_message_text # For direct display in partial
854
- return templates.TemplateResponse("partials/_load_manager_view.html", context, headers=response_headers)
842
+ return templates.TemplateResponse(request, "partials/_load_manager_view.html", context, headers=response_headers)
855
843
 
856
844
  @app.post("/ui/load-flock-action/by-upload", response_class=HTMLResponse, tags=["UI Actions"])
857
845
  async def ui_load_flock_by_upload_action(request: Request, flock_file_upload: UploadFile = File(...)):
@@ -876,35 +864,73 @@ async def ui_load_flock_by_upload_action(request: Request, flock_file_upload: Up
876
864
  response_headers["HX-Push-Url"] = f"/ui/editor/execute?ui_mode={ui_mode_query}"
877
865
  response_headers["HX-Trigger"] = json.dumps({"flockLoaded": None, "flockFileListChanged": None, "notify": {"type": "success", "message": success_message_text}})
878
866
  context = get_base_context_web(request, success=success_message_text, ui_mode=ui_mode_query)
879
- return templates.TemplateResponse("partials/_execution_view_container.html", context, headers=response_headers)
867
+ return templates.TemplateResponse(request, "partials/_execution_view_container.html", context, headers=response_headers)
880
868
  else: error_message_text = f"Failed to process uploaded '{filename_to_load}'."
881
869
 
882
870
  final_error_msg = error_message_text or "Upload failed."
883
871
  response_headers["HX-Trigger"] = json.dumps({"notify": {"type": "error", "message": final_error_msg}})
884
872
  context = get_base_context_web(request, error=final_error_msg, ui_mode=ui_mode_query)
885
- return templates.TemplateResponse("partials/_create_flock_form.html", context, headers=response_headers)
873
+ return templates.TemplateResponse(request, "partials/_create_flock_form.html", context, headers=response_headers)
886
874
 
887
875
  @app.post("/ui/create-flock", response_class=HTMLResponse, tags=["UI Actions"])
888
876
  async def ui_create_flock_action(request: Request, flock_name: str = Form(...), default_model: str = Form(None), description: str = Form(None)):
889
877
  ui_mode_query = request.query_params.get("ui_mode", "standalone")
890
878
  if not flock_name.strip():
891
879
  context = get_base_context_web(request, error="Flock name cannot be empty.", ui_mode=ui_mode_query)
892
- return templates.TemplateResponse("partials/_create_flock_form.html", context)
880
+ return templates.TemplateResponse(request, "partials/_create_flock_form.html", context)
893
881
 
894
882
  new_flock = create_new_flock_service(flock_name, default_model, description, request.app.state)
895
883
  success_msg_text = f"New flock '{new_flock.name}' created. Navigating to Execute page. Configure properties and agents as needed."
896
884
  response_headers = {"HX-Push-Url": f"/ui/editor/execute?ui_mode={ui_mode_query}", "HX-Trigger": json.dumps({"flockLoaded": None, "notify": {"type": "success", "message": success_msg_text}})}
897
885
  context = get_base_context_web(request, success=success_msg_text, ui_mode=ui_mode_query)
898
- return templates.TemplateResponse("partials/_execution_view_container.html", context, headers=response_headers)
886
+ return templates.TemplateResponse(request, "partials/_execution_view_container.html", context, headers=response_headers)
899
887
 
900
888
 
901
889
  # --- Settings Page & Endpoints ---
890
+
891
+ @app.get("/ui/htmx/live-logs", response_class=HTMLResponse, tags=["UI HTMX Partials"])
892
+ async def htmx_live_cli_logs(request: Request, limit: int = Query(200, ge=50, le=800)):
893
+ store = get_live_log_store()
894
+ entries = store.get_entries(limit=limit)
895
+
896
+ hide_polling_param = request.query_params.get("hide_polling", "1")
897
+ hide_polling = str(hide_polling_param).lower() not in {"0", "false", "off", ""}
898
+
899
+ if hide_polling:
900
+ entries = [entry for entry in entries if "GET /ui/htmx/live-logs" not in str(entry.get("text", ""))]
901
+
902
+ logs = []
903
+ for entry in entries:
904
+ raw_timestamp = float(entry.get("timestamp", 0.0) or 0.0)
905
+ time_display = datetime.fromtimestamp(raw_timestamp).strftime("%H:%M:%S")
906
+ stream = str(entry.get("stream", "stdout") or "stdout")
907
+ css_class = "stderr" if stream == "stderr" else "stdout"
908
+ stream_label = "STDERR" if stream == "stderr" else "STDOUT"
909
+ text_value = str(entry.get("text", ""))
910
+ logs.append(
911
+ {
912
+ "time_display": time_display,
913
+ "stream_label": stream_label,
914
+ "css_class": css_class,
915
+ "text": text_value,
916
+ }
917
+ )
918
+
919
+ latest_timestamp = entries[-1]["timestamp"] if entries else None
920
+ return templates.TemplateResponse(request, "partials/_live_logs.html",
921
+ {
922
+ "request": request,
923
+ "logs": logs,
924
+ "latest_timestamp": latest_timestamp,
925
+ },
926
+ )
927
+
902
928
  @app.get("/ui/settings", response_class=HTMLResponse, tags=["UI Pages"])
903
929
  async def page_settings(request: Request, error: str = None, success: str = None, ui_mode: str = Query("standalone")):
904
930
  context = get_base_context_web(request, error, success, ui_mode)
905
931
  root_path = request.scope.get("root_path", "")
906
932
  context["initial_content_url"] = f"{root_path}/ui/htmx/settings-view"
907
- return templates.TemplateResponse("base.html", context)
933
+ return templates.TemplateResponse(request, "base.html", context)
908
934
 
909
935
  def _prepare_env_vars_for_template_web():
910
936
  env_vars_raw = load_env_file_web(); show_secrets = get_show_secrets_setting_web(env_vars_raw)
@@ -920,21 +946,21 @@ async def htmx_get_settings_view(request: Request):
920
946
  env_vars_list, show_secrets = _prepare_env_vars_for_template_web()
921
947
  theme_name = get_current_theme_name()
922
948
  themes_available = [p.stem for p in THEMES_DIR.glob("*.toml")] if THEMES_DIR and THEMES_DIR.exists() else []
923
- return templates.TemplateResponse("partials/_settings_view.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets, "themes": themes_available, "current_theme": theme_name})
949
+ return templates.TemplateResponse(request, "partials/_settings_view.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets, "themes": themes_available, "current_theme": theme_name})
924
950
 
925
951
  @app.post("/ui/htmx/toggle-show-secrets", response_class=HTMLResponse, tags=["UI Actions"])
926
952
  async def htmx_toggle_show_secrets(request: Request):
927
953
  env_vars_raw = load_env_file_web(); current = get_show_secrets_setting_web(env_vars_raw)
928
954
  set_show_secrets_setting_web(not current)
929
955
  env_vars_list, show_secrets = _prepare_env_vars_for_template_web()
930
- return templates.TemplateResponse("partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
956
+ return templates.TemplateResponse(request, "partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
931
957
 
932
958
  @app.post("/ui/htmx/env-delete", response_class=HTMLResponse, tags=["UI Actions"])
933
959
  async def htmx_env_delete(request: Request, var_name: str = Form(...)):
934
960
  env_vars_raw = load_env_file_web()
935
961
  if var_name in env_vars_raw: del env_vars_raw[var_name]; save_env_file_web(env_vars_raw)
936
962
  env_vars_list, show_secrets = _prepare_env_vars_for_template_web()
937
- return templates.TemplateResponse("partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
963
+ return templates.TemplateResponse(request, "partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
938
964
 
939
965
  @app.post("/ui/htmx/env-edit", response_class=HTMLResponse, tags=["UI Actions"])
940
966
  async def htmx_env_edit(request: Request, var_name: str = Form(...)):
@@ -945,7 +971,7 @@ async def htmx_env_edit(request: Request, var_name: str = Form(...)):
945
971
  env_vars_raw[var_name] = new_value
946
972
  save_env_file_web(env_vars_raw)
947
973
  env_vars_list, show_secrets = _prepare_env_vars_for_template_web()
948
- return templates.TemplateResponse("partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
974
+ return templates.TemplateResponse(request, "partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
949
975
 
950
976
  @app.get("/ui/htmx/env-add-form", response_class=HTMLResponse, tags=["UI HTMX Partials"])
951
977
  async def htmx_env_add_form(request: Request):
@@ -956,7 +982,7 @@ async def htmx_env_add(request: Request, var_name: str = Form(...), var_value: s
956
982
  env_vars_raw = load_env_file_web()
957
983
  env_vars_raw[var_name] = var_value; save_env_file_web(env_vars_raw)
958
984
  env_vars_list, show_secrets = _prepare_env_vars_for_template_web()
959
- return templates.TemplateResponse("partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
985
+ return templates.TemplateResponse(request, "partials/_env_vars_table.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
960
986
 
961
987
  @app.get("/ui/htmx/theme-preview", response_class=HTMLResponse, tags=["UI HTMX Partials"])
962
988
  async def htmx_theme_preview(request: Request, theme: str = Query(None)):
@@ -1010,7 +1036,7 @@ async def htmx_theme_preview(request: Request, theme: str = Query(None)):
1010
1036
 
1011
1037
  css_vars_str = ":root {\n" + "\\n".join([f" {k}: {v};" for k, v in css_vars.items()]) + "\\n}"
1012
1038
  main_colors = [("Background", css_vars.get("--pico-background-color")), ("Text", css_vars.get("--pico-color")), ("Primary", css_vars.get("--pico-primary")), ("Secondary", css_vars.get("--pico-secondary")), ("Muted", css_vars.get("--pico-muted-color"))]
1013
- return templates.TemplateResponse("partials/_theme_preview.html", {"request": request, "theme_name": theme_name_for_display, "css_vars_str": css_vars_str, "main_colors": main_colors})
1039
+ return templates.TemplateResponse(request, "partials/_theme_preview.html", {"request": request, "theme_name": theme_name_for_display, "css_vars_str": css_vars_str, "main_colors": main_colors})
1014
1040
 
1015
1041
  @app.post("/ui/apply-theme", tags=["UI Actions"])
1016
1042
  async def apply_theme(request: Request, theme: str = Form(...)):
@@ -1024,24 +1050,24 @@ async def apply_theme(request: Request, theme: str = Form(...)):
1024
1050
  @app.get("/ui/htmx/settings/env-vars", response_class=HTMLResponse, tags=["UI HTMX Partials"])
1025
1051
  async def htmx_settings_env_vars(request: Request):
1026
1052
  env_vars_list, show_secrets = _prepare_env_vars_for_template_web()
1027
- return templates.TemplateResponse("partials/_settings_env_content.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
1053
+ return templates.TemplateResponse(request, "partials/_settings_env_content.html", {"request": request, "env_vars": env_vars_list, "show_secrets": show_secrets})
1028
1054
 
1029
1055
  @app.get("/ui/htmx/settings/theme", response_class=HTMLResponse, tags=["UI HTMX Partials"])
1030
1056
  async def htmx_settings_theme(request: Request):
1031
1057
  theme_name = get_current_theme_name()
1032
1058
  themes_available = [p.stem for p in THEMES_DIR.glob("*.toml")] if THEMES_DIR and THEMES_DIR.exists() else []
1033
- return templates.TemplateResponse("partials/_settings_theme_content.html", {"request": request, "themes": themes_available, "current_theme": theme_name})
1059
+ return templates.TemplateResponse(request, "partials/_settings_theme_content.html", {"request": request, "themes": themes_available, "current_theme": theme_name})
1034
1060
 
1035
1061
  @app.get("/ui/chat", response_class=HTMLResponse, tags=["UI Pages"])
1036
1062
  async def page_chat(request: Request, ui_mode: str = Query("standalone")):
1037
1063
  context = get_base_context_web(request, ui_mode=ui_mode)
1038
1064
  context["initial_content_url"] = "/ui/htmx/chat-view"
1039
- return templates.TemplateResponse("base.html", context)
1065
+ return templates.TemplateResponse(request, "base.html", context)
1040
1066
 
1041
1067
  @app.get("/ui/htmx/chat-view", response_class=HTMLResponse, tags=["UI HTMX Partials"])
1042
1068
  async def htmx_get_chat_view(request: Request):
1043
1069
  # Render container partial; session handled in chat router
1044
- return templates.TemplateResponse("partials/_chat_container.html", get_base_context_web(request))
1070
+ return templates.TemplateResponse(request, "partials/_chat_container.html", get_base_context_web(request))
1045
1071
 
1046
1072
  if __name__ == "__main__":
1047
1073
  import uvicorn
@@ -773,8 +773,7 @@ function example() {
773
773
  "passes": ratio >= 4.5,
774
774
  })
775
775
 
776
- return templates.TemplateResponse(
777
- "theme_mapper.html",
776
+ return templates.TemplateResponse(request, "theme_mapper.html",
778
777
  {
779
778
  "request": request,
780
779
  "css_vars": css_vars_str,
flock/webapp/run.py CHANGED
@@ -47,6 +47,7 @@ def start_unified_server(
47
47
  # Import necessary webapp components HERE, after path setup.
48
48
  from flock.core.api.run_store import RunStore
49
49
  from flock.core.logging.logging import get_logger # For logging
50
+ from flock.core.logging.live_capture import enable_live_log_capture
50
51
  from flock.webapp.app.config import ( # For logging resolved theme
51
52
  get_current_theme_name,
52
53
  set_current_theme_name,
@@ -60,6 +61,9 @@ def start_unified_server(
60
61
  )
61
62
 
62
63
  logger = get_logger("webapp.run") # Use a logger
64
+ enable_live_log_capture()
65
+ logger.info("Live CLI log capture enabled for web footer display.")
66
+
63
67
 
64
68
  # 1. Set UI Theme globally for the webapp
65
69
  set_current_theme_name(ui_theme)