flock-core 0.4.540__py3-none-any.whl → 0.4.542__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/core/logging/live_capture.py +137 -0
- flock/tools/zendesk_tools.py +1 -1
- flock/webapp/app/api/agent_management.py +8 -12
- flock/webapp/app/api/execution.py +4 -8
- flock/webapp/app/api/flock_management.py +4 -8
- flock/webapp/app/api/registry_viewer.py +1 -2
- flock/webapp/app/chat.py +14 -17
- flock/webapp/app/main.py +84 -58
- flock/webapp/app/theme_mapper.py +1 -2
- flock/webapp/run.py +4 -0
- flock/webapp/static/css/layout.css +239 -4
- flock/webapp/templates/base.html +192 -3
- flock/webapp/templates/partials/_live_logs.html +13 -0
- {flock_core-0.4.540.dist-info → flock_core-0.4.542.dist-info}/METADATA +1 -1
- {flock_core-0.4.540.dist-info → flock_core-0.4.542.dist-info}/RECORD +18 -16
- {flock_core-0.4.540.dist-info → flock_core-0.4.542.dist-info}/WHEEL +0 -0
- {flock_core-0.4.540.dist-info → flock_core-0.4.542.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.540.dist-info → flock_core-0.4.542.dist-info}/licenses/LICENSE +0 -0
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
|
flock/webapp/app/theme_mapper.py
CHANGED
|
@@ -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)
|