refactor: redesign finished dialogs and UI elements
This commit is contained in:
@@ -36,7 +36,7 @@ Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
width: 25%;
|
width: 20%;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
margin-left: 1;
|
margin-left: 1;
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ VulnerabilityDetailScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#chat_area_container {
|
#chat_area_container {
|
||||||
width: 75%;
|
width: 80%;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,30 +24,26 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
start_text = Text()
|
start_text = Text()
|
||||||
start_text.append("🦉 ", style="bold white")
|
start_text.append("Penetration test initiated", style="bold #22c55e")
|
||||||
start_text.append("STRIX CYBERSECURITY AGENT", style="bold green")
|
|
||||||
|
|
||||||
target_text = Text()
|
target_text = Text()
|
||||||
|
target_text.append("Target", style="dim")
|
||||||
|
target_text.append(" ")
|
||||||
if len(args.targets_info) == 1:
|
if len(args.targets_info) == 1:
|
||||||
target_text.append("🎯 Target: ", style="bold cyan")
|
|
||||||
target_text.append(args.targets_info[0]["original"], style="bold white")
|
target_text.append(args.targets_info[0]["original"], style="bold white")
|
||||||
else:
|
else:
|
||||||
target_text.append("🎯 Targets: ", style="bold cyan")
|
target_text.append(f"{len(args.targets_info)} targets", style="bold white")
|
||||||
target_text.append(f"{len(args.targets_info)} targets\n", style="bold white")
|
for target_info in args.targets_info:
|
||||||
for i, target_info in enumerate(args.targets_info):
|
target_text.append("\n ")
|
||||||
target_text.append(" • ", style="dim white")
|
|
||||||
target_text.append(target_info["original"], style="white")
|
target_text.append(target_info["original"], style="white")
|
||||||
if i < len(args.targets_info) - 1:
|
|
||||||
target_text.append("\n")
|
|
||||||
|
|
||||||
results_text = Text()
|
results_text = Text()
|
||||||
results_text.append("📊 Results will be saved to: ", style="bold cyan")
|
results_text.append("Output", style="dim")
|
||||||
results_text.append(f"strix_runs/{args.run_name}", style="bold white")
|
results_text.append(" ")
|
||||||
|
results_text.append(f"strix_runs/{args.run_name}", style="#60a5fa")
|
||||||
|
|
||||||
note_text = Text()
|
note_text = Text()
|
||||||
note_text.append("\n\n", style="dim")
|
note_text.append("\n\n", style="dim")
|
||||||
note_text.append("⏱️ ", style="dim")
|
|
||||||
note_text.append("This may take a while depending on target complexity. ", style="dim")
|
|
||||||
note_text.append("Vulnerabilities will be displayed in real-time.", style="dim")
|
note_text.append("Vulnerabilities will be displayed in real-time.", style="dim")
|
||||||
|
|
||||||
startup_panel = Panel(
|
startup_panel = Panel(
|
||||||
@@ -59,9 +55,9 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||||||
results_text,
|
results_text,
|
||||||
note_text,
|
note_text,
|
||||||
),
|
),
|
||||||
title="[bold green]🛡️ STRIX PENETRATION TEST INITIATED",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="green",
|
border_style="#22c55e",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -126,8 +122,7 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||||||
|
|
||||||
def create_live_status() -> Panel:
|
def create_live_status() -> Panel:
|
||||||
status_text = Text()
|
status_text = Text()
|
||||||
status_text.append("🦉 ", style="bold white")
|
status_text.append("Penetration test in progress", style="bold #22c55e")
|
||||||
status_text.append("Running penetration test...", style="bold #22c55e")
|
|
||||||
status_text.append("\n\n")
|
status_text.append("\n\n")
|
||||||
|
|
||||||
stats_text = build_live_stats_text(tracer, agent_config)
|
stats_text = build_live_stats_text(tracer, agent_config)
|
||||||
@@ -136,8 +131,8 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||||||
|
|
||||||
return Panel(
|
return Panel(
|
||||||
status_text,
|
status_text,
|
||||||
title="[bold #22c55e]🔍 Live Penetration Test Status",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="#22c55e",
|
border_style="#22c55e",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -169,7 +164,7 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||||||
error_msg = result.get("error", "Unknown error")
|
error_msg = result.get("error", "Unknown error")
|
||||||
error_details = result.get("details")
|
error_details = result.get("details")
|
||||||
console.print()
|
console.print()
|
||||||
console.print(f"[bold red]❌ Penetration test failed:[/] {error_msg}")
|
console.print(f"[bold red]Penetration test failed:[/] {error_msg}")
|
||||||
if error_details:
|
if error_details:
|
||||||
console.print(f"[dim]{error_details}[/]")
|
console.print(f"[dim]{error_details}[/]")
|
||||||
console.print()
|
console.print()
|
||||||
@@ -186,8 +181,7 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
final_report_text = Text()
|
final_report_text = Text()
|
||||||
final_report_text.append("📄 ", style="bold cyan")
|
final_report_text.append("Penetration test summary", style="bold #60a5fa")
|
||||||
final_report_text.append("FINAL PENETRATION TEST REPORT", style="bold cyan")
|
|
||||||
|
|
||||||
final_report_panel = Panel(
|
final_report_panel = Panel(
|
||||||
Text.assemble(
|
Text.assemble(
|
||||||
@@ -195,9 +189,9 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|||||||
"\n\n",
|
"\n\n",
|
||||||
tracer.final_scan_result,
|
tracer.final_scan_result,
|
||||||
),
|
),
|
||||||
title="[bold cyan]📊 PENETRATION TEST SUMMARY",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="cyan",
|
border_style="#60a5fa",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ def validate_environment() -> None: # noqa: PLR0912, PLR0915
|
|||||||
|
|
||||||
if missing_required_vars:
|
if missing_required_vars:
|
||||||
error_text = Text()
|
error_text = Text()
|
||||||
error_text.append("❌ ", style="bold red")
|
|
||||||
error_text.append("MISSING REQUIRED ENVIRONMENT VARIABLES", style="bold red")
|
error_text.append("MISSING REQUIRED ENVIRONMENT VARIABLES", style="bold red")
|
||||||
error_text.append("\n\n", style="white")
|
error_text.append("\n\n", style="white")
|
||||||
|
|
||||||
@@ -163,8 +162,8 @@ def validate_environment() -> None: # noqa: PLR0912, PLR0915
|
|||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
error_text,
|
error_text,
|
||||||
title="[bold red]🛡️ STRIX CONFIGURATION ERROR",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="red",
|
border_style="red",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -179,7 +178,6 @@ def check_docker_installed() -> None:
|
|||||||
if shutil.which("docker") is None:
|
if shutil.which("docker") is None:
|
||||||
console = Console()
|
console = Console()
|
||||||
error_text = Text()
|
error_text = Text()
|
||||||
error_text.append("❌ ", style="bold red")
|
|
||||||
error_text.append("DOCKER NOT INSTALLED", style="bold red")
|
error_text.append("DOCKER NOT INSTALLED", style="bold red")
|
||||||
error_text.append("\n\n", style="white")
|
error_text.append("\n\n", style="white")
|
||||||
error_text.append("The 'docker' CLI was not found in your PATH.\n", style="white")
|
error_text.append("The 'docker' CLI was not found in your PATH.\n", style="white")
|
||||||
@@ -189,8 +187,8 @@ def check_docker_installed() -> None:
|
|||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
error_text,
|
error_text,
|
||||||
title="[bold red]🛡️ STRIX STARTUP ERROR",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="red",
|
border_style="red",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -234,7 +232,6 @@ async def warm_up_llm() -> None:
|
|||||||
|
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
error_text = Text()
|
error_text = Text()
|
||||||
error_text.append("❌ ", style="bold red")
|
|
||||||
error_text.append("LLM CONNECTION FAILED", style="bold red")
|
error_text.append("LLM CONNECTION FAILED", style="bold red")
|
||||||
error_text.append("\n\n", style="white")
|
error_text.append("\n\n", style="white")
|
||||||
error_text.append("Could not establish connection to the language model.\n", style="white")
|
error_text.append("Could not establish connection to the language model.\n", style="white")
|
||||||
@@ -243,8 +240,8 @@ async def warm_up_llm() -> None:
|
|||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
error_text,
|
error_text,
|
||||||
title="[bold red]🛡️ STRIX STARTUP ERROR",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="red",
|
border_style="red",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -410,30 +407,22 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) ->
|
|||||||
|
|
||||||
completion_text = Text()
|
completion_text = Text()
|
||||||
if scan_completed:
|
if scan_completed:
|
||||||
completion_text.append("🦉 ", style="bold white")
|
completion_text.append("Penetration test completed", style="bold #22c55e")
|
||||||
completion_text.append("AGENT FINISHED", style="bold green")
|
|
||||||
completion_text.append(" • ", style="dim white")
|
|
||||||
completion_text.append("Penetration test completed", style="white")
|
|
||||||
else:
|
else:
|
||||||
completion_text.append("🦉 ", style="bold white")
|
completion_text.append("SESSION ENDED", style="bold #eab308")
|
||||||
completion_text.append("SESSION ENDED", style="bold yellow")
|
|
||||||
completion_text.append(" • ", style="dim white")
|
|
||||||
completion_text.append("Penetration test interrupted by user", style="white")
|
|
||||||
|
|
||||||
stats_text = build_final_stats_text(tracer)
|
|
||||||
|
|
||||||
target_text = Text()
|
target_text = Text()
|
||||||
|
target_text.append("Target", style="dim")
|
||||||
|
target_text.append(" ")
|
||||||
if len(args.targets_info) == 1:
|
if len(args.targets_info) == 1:
|
||||||
target_text.append("🎯 Target: ", style="bold cyan")
|
|
||||||
target_text.append(args.targets_info[0]["original"], style="bold white")
|
target_text.append(args.targets_info[0]["original"], style="bold white")
|
||||||
else:
|
else:
|
||||||
target_text.append("🎯 Targets: ", style="bold cyan")
|
target_text.append(f"{len(args.targets_info)} targets", style="bold white")
|
||||||
target_text.append(f"{len(args.targets_info)} targets\n", style="bold white")
|
for target_info in args.targets_info:
|
||||||
for i, target_info in enumerate(args.targets_info):
|
target_text.append("\n ")
|
||||||
target_text.append(" • ", style="dim white")
|
|
||||||
target_text.append(target_info["original"], style="white")
|
target_text.append(target_info["original"], style="white")
|
||||||
if i < len(args.targets_info) - 1:
|
|
||||||
target_text.append("\n")
|
stats_text = build_final_stats_text(tracer)
|
||||||
|
|
||||||
panel_parts = [completion_text, "\n\n", target_text]
|
panel_parts = [completion_text, "\n\n", target_text]
|
||||||
|
|
||||||
@@ -442,18 +431,20 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) ->
|
|||||||
|
|
||||||
if scan_completed or has_vulnerabilities:
|
if scan_completed or has_vulnerabilities:
|
||||||
results_text = Text()
|
results_text = Text()
|
||||||
results_text.append("📊 Results Saved To: ", style="bold cyan")
|
results_text.append("\n")
|
||||||
results_text.append(str(results_path), style="bold yellow")
|
results_text.append("Output", style="dim")
|
||||||
panel_parts.extend(["\n\n", results_text])
|
results_text.append(" ")
|
||||||
|
results_text.append(str(results_path), style="#60a5fa")
|
||||||
|
panel_parts.extend(["\n", results_text])
|
||||||
|
|
||||||
panel_content = Text.assemble(*panel_parts)
|
panel_content = Text.assemble(*panel_parts)
|
||||||
|
|
||||||
border_style = "green" if scan_completed else "yellow"
|
border_style = "#22c55e" if scan_completed else "#eab308"
|
||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
panel_content,
|
panel_content,
|
||||||
title="[bold green]🛡️ STRIX CYBERSECURITY AGENT",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style=border_style,
|
border_style=border_style,
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -461,8 +452,7 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) ->
|
|||||||
console.print("\n")
|
console.print("\n")
|
||||||
console.print(panel)
|
console.print(panel)
|
||||||
console.print()
|
console.print()
|
||||||
console.print("[dim]🌐 Website:[/] [cyan]https://strix.ai[/]")
|
console.print("[#60a5fa]strix.ai[/] [dim]·[/] [#60a5fa]discord.gg/YjKFvEZSdZ[/]")
|
||||||
console.print("[dim]💬 Discord:[/] [cyan]https://discord.gg/YjKFvEZSdZ[/]")
|
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
|
|
||||||
@@ -474,7 +464,7 @@ def pull_docker_image() -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
console.print()
|
console.print()
|
||||||
console.print(f"[bold cyan]🐳 Pulling Docker image:[/] {Config.get('strix_image')}")
|
console.print(f"[dim]Pulling image[/] {Config.get('strix_image')}")
|
||||||
console.print("[dim yellow]This only happens on first run and may take a few minutes...[/]")
|
console.print("[dim yellow]This only happens on first run and may take a few minutes...[/]")
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
@@ -489,7 +479,6 @@ def pull_docker_image() -> None:
|
|||||||
except DockerException as e:
|
except DockerException as e:
|
||||||
console.print()
|
console.print()
|
||||||
error_text = Text()
|
error_text = Text()
|
||||||
error_text.append("❌ ", style="bold red")
|
|
||||||
error_text.append("FAILED TO PULL IMAGE", style="bold red")
|
error_text.append("FAILED TO PULL IMAGE", style="bold red")
|
||||||
error_text.append("\n\n", style="white")
|
error_text.append("\n\n", style="white")
|
||||||
error_text.append(f"Could not download: {Config.get('strix_image')}\n", style="white")
|
error_text.append(f"Could not download: {Config.get('strix_image')}\n", style="white")
|
||||||
@@ -497,8 +486,8 @@ def pull_docker_image() -> None:
|
|||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
error_text,
|
error_text,
|
||||||
title="[bold red]🛡️ DOCKER PULL ERROR",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="red",
|
border_style="red",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -506,8 +495,7 @@ def pull_docker_image() -> None:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
success_text = Text()
|
success_text = Text()
|
||||||
success_text.append("✅ ", style="bold green")
|
success_text.append("Docker image ready", style="#22c55e")
|
||||||
success_text.append("Successfully pulled Docker image", style="green")
|
|
||||||
console.print(success_text)
|
console.print(success_text)
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
|
|||||||
@@ -92,12 +92,13 @@ class AgentFinishRenderer(BaseToolRenderer):
|
|||||||
success = args.get("success", True)
|
success = args.get("success", True)
|
||||||
|
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("🏁 ")
|
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
text.append("Agent completed", style="bold #fbbf24")
|
text.append("◆ ", style="#22c55e")
|
||||||
|
text.append("Agent completed", style="bold #22c55e")
|
||||||
else:
|
else:
|
||||||
text.append("Agent failed", style="bold #fbbf24")
|
text.append("◆ ", style="#ef4444")
|
||||||
|
text.append("Agent failed", style="bold #ef4444")
|
||||||
|
|
||||||
if result_summary:
|
if result_summary:
|
||||||
text.append("\n ")
|
text.append("\n ")
|
||||||
|
|||||||
@@ -65,16 +65,16 @@ class StrReplaceEditorRenderer(BaseToolRenderer):
|
|||||||
text = Text()
|
text = Text()
|
||||||
|
|
||||||
icons_and_labels = {
|
icons_and_labels = {
|
||||||
"view": ("📖 ", "Reading file", "#10b981"),
|
"view": ("◇ ", "read", "#10b981"),
|
||||||
"str_replace": ("✏️ ", "Editing file", "#10b981"),
|
"str_replace": ("◇ ", "edit", "#10b981"),
|
||||||
"create": ("📝 ", "Creating file", "#10b981"),
|
"create": ("◇ ", "create", "#10b981"),
|
||||||
"insert": ("✏️ ", "Inserting text", "#10b981"),
|
"insert": ("◇ ", "insert", "#10b981"),
|
||||||
"undo_edit": ("↩️ ", "Undoing edit", "#10b981"),
|
"undo_edit": ("◇ ", "undo", "#10b981"),
|
||||||
}
|
}
|
||||||
|
|
||||||
icon, label, color = icons_and_labels.get(command, ("📄 ", "File operation", "#10b981"))
|
icon, label, color = icons_and_labels.get(command, ("◇ ", "file", "#10b981"))
|
||||||
text.append(icon)
|
text.append(icon, style=color)
|
||||||
text.append(label, style=f"bold {color}")
|
text.append(label, style="dim")
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
path_display = path[-60:] if len(path) > 60 else path
|
path_display = path[-60:] if len(path) > 60 else path
|
||||||
@@ -158,23 +158,20 @@ class SearchFilesRenderer(BaseToolRenderer):
|
|||||||
regex = args.get("regex", "")
|
regex = args.get("regex", "")
|
||||||
|
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("🔍 ")
|
text.append("◇ ", style="#a855f7")
|
||||||
text.append("Searching files", style="bold purple")
|
text.append("search", style="dim")
|
||||||
text.append(" ")
|
text.append(" ")
|
||||||
|
|
||||||
if path and regex:
|
if path and regex:
|
||||||
text.append(path, style="dim")
|
text.append(path, style="dim")
|
||||||
text.append(" for '", style="dim")
|
text.append(" ", style="dim")
|
||||||
text.append(regex, style="dim")
|
text.append(regex, style="#a855f7")
|
||||||
text.append("'", style="dim")
|
|
||||||
elif path:
|
elif path:
|
||||||
text.append(path, style="dim")
|
text.append(path, style="dim")
|
||||||
elif regex:
|
elif regex:
|
||||||
text.append("'", style="dim")
|
text.append(regex, style="#a855f7")
|
||||||
text.append(regex, style="dim")
|
|
||||||
text.append("'", style="dim")
|
|
||||||
else:
|
else:
|
||||||
text.append("Searching...", style="dim")
|
text.append("...", style="dim")
|
||||||
|
|
||||||
css_classes = cls.get_css_classes("completed")
|
css_classes = cls.get_css_classes("completed")
|
||||||
return Static(text, classes=css_classes)
|
return Static(text, classes=css_classes)
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class FinishScanRenderer(BaseToolRenderer):
|
|||||||
recommendations = args.get("recommendations", "")
|
recommendations = args.get("recommendations", "")
|
||||||
|
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("🏁 ")
|
text.append("◆ ", style="#22c55e")
|
||||||
text.append("Finishing Scan", style="bold #dc2626")
|
text.append("Penetration test completed", style="bold #22c55e")
|
||||||
|
|
||||||
if executive_summary:
|
if executive_summary:
|
||||||
text.append("\n\n")
|
text.append("\n\n")
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ class CreateNoteRenderer(BaseToolRenderer):
|
|||||||
category = args.get("category", "general")
|
category = args.get("category", "general")
|
||||||
|
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("📝 ")
|
text.append("◇ ", style="#fbbf24")
|
||||||
text.append("Note", style="bold #fbbf24")
|
text.append("note", style="dim")
|
||||||
text.append(" ")
|
text.append(" ")
|
||||||
text.append(f"({category})", style="dim")
|
text.append(f"({category})", style="dim")
|
||||||
|
|
||||||
@@ -50,8 +50,8 @@ class DeleteNoteRenderer(BaseToolRenderer):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def render(cls, tool_data: dict[str, Any]) -> Static: # noqa: ARG003
|
def render(cls, tool_data: dict[str, Any]) -> Static: # noqa: ARG003
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("📝 ")
|
text.append("◇ ", style="#fbbf24")
|
||||||
text.append("Note Removed", style="bold #94a3b8")
|
text.append("note removed", style="dim")
|
||||||
|
|
||||||
css_classes = cls.get_css_classes("completed")
|
css_classes = cls.get_css_classes("completed")
|
||||||
return Static(text, classes=css_classes)
|
return Static(text, classes=css_classes)
|
||||||
@@ -70,8 +70,8 @@ class UpdateNoteRenderer(BaseToolRenderer):
|
|||||||
content = args.get("content")
|
content = args.get("content")
|
||||||
|
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("📝 ")
|
text.append("◇ ", style="#fbbf24")
|
||||||
text.append("Note Updated", style="bold #fbbf24")
|
text.append("note updated", style="dim")
|
||||||
|
|
||||||
if title:
|
if title:
|
||||||
text.append("\n ")
|
text.append("\n ")
|
||||||
@@ -99,8 +99,8 @@ class ListNotesRenderer(BaseToolRenderer):
|
|||||||
result = tool_data.get("result")
|
result = tool_data.get("result")
|
||||||
|
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("📝 ")
|
text.append("◇ ", style="#fbbf24")
|
||||||
text.append("Notes", style="bold #fbbf24")
|
text.append("notes", style="dim")
|
||||||
|
|
||||||
if isinstance(result, str) and result.strip():
|
if isinstance(result, str) and result.strip():
|
||||||
text.append("\n ")
|
text.append("\n ")
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ class ScanStartInfoRenderer(BaseToolRenderer):
|
|||||||
targets = args.get("targets", [])
|
targets = args.get("targets", [])
|
||||||
|
|
||||||
text = Text()
|
text = Text()
|
||||||
text.append("🚀 Starting penetration test")
|
text.append("◈ ", style="#22c55e")
|
||||||
|
text.append("Starting penetration test")
|
||||||
|
|
||||||
if len(targets) == 1:
|
if len(targets) == 1:
|
||||||
text.append(" on ")
|
text.append(" on ")
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class SplashScreen(Static): # type: ignore[misc]
|
|||||||
class HelpScreen(ModalScreen): # type: ignore[misc]
|
class HelpScreen(ModalScreen): # type: ignore[misc]
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Grid(
|
yield Grid(
|
||||||
Label("🦉 Strix Help", id="help_title"),
|
Label("Strix Help", id="help_title"),
|
||||||
Label(
|
Label(
|
||||||
"F1 Help\nCtrl+Q/C Quit\nESC Stop Agent\n"
|
"F1 Help\nCtrl+Q/C Quit\nESC Stop Agent\n"
|
||||||
"Enter Send message to agent\nTab Switch panels\n↑/↓ Navigate tree",
|
"Enter Send message to agent\nTab Switch panels\n↑/↓ Navigate tree",
|
||||||
@@ -668,7 +668,7 @@ class QuitScreen(ModalScreen): # type: ignore[misc]
|
|||||||
class StrixTUIApp(App): # type: ignore[misc]
|
class StrixTUIApp(App): # type: ignore[misc]
|
||||||
CSS_PATH = "assets/tui_styles.tcss"
|
CSS_PATH = "assets/tui_styles.tcss"
|
||||||
|
|
||||||
SIDEBAR_MIN_WIDTH = 100
|
SIDEBAR_MIN_WIDTH = 140
|
||||||
|
|
||||||
selected_agent_id: reactive[str | None] = reactive(default=None)
|
selected_agent_id: reactive[str | None] = reactive(default=None)
|
||||||
show_splash: reactive[bool] = reactive(default=True)
|
show_splash: reactive[bool] = reactive(default=True)
|
||||||
@@ -795,7 +795,7 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||||||
chat_input.set_app_reference(self)
|
chat_input.set_app_reference(self)
|
||||||
chat_input_container = Horizontal(chat_prompt, chat_input, id="chat_input_container")
|
chat_input_container = Horizontal(chat_prompt, chat_input, id="chat_input_container")
|
||||||
|
|
||||||
agents_tree = Tree("🤖 Active Agents", id="agents_tree")
|
agents_tree = Tree("Agents", id="agents_tree")
|
||||||
agents_tree.root.expand()
|
agents_tree.root.expand()
|
||||||
agents_tree.show_root = False
|
agents_tree.show_root = False
|
||||||
|
|
||||||
@@ -911,16 +911,16 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||||||
status = agent_data.get("status", "running")
|
status = agent_data.get("status", "running")
|
||||||
|
|
||||||
status_indicators = {
|
status_indicators = {
|
||||||
"running": "🟢",
|
"running": "●",
|
||||||
"waiting": "⏸️",
|
"waiting": "○",
|
||||||
"completed": "✅",
|
"completed": "◆",
|
||||||
"failed": "❌",
|
"failed": "◇",
|
||||||
"stopped": "⏹️",
|
"stopped": "■",
|
||||||
"stopping": "⏸️",
|
"stopping": "○",
|
||||||
"llm_failed": "🔴",
|
"llm_failed": "◇",
|
||||||
}
|
}
|
||||||
|
|
||||||
status_icon = status_indicators.get(status, "🔵")
|
status_icon = status_indicators.get(status, "○")
|
||||||
vuln_count = self._agent_vulnerability_count(agent_id)
|
vuln_count = self._agent_vulnerability_count(agent_id)
|
||||||
vuln_indicator = f" ({vuln_count})" if vuln_count > 0 else ""
|
vuln_indicator = f" ({vuln_count})" if vuln_count > 0 else ""
|
||||||
agent_name = f"{status_icon} {agent_name_raw}{vuln_indicator}"
|
agent_name = f"{status_icon} {agent_name_raw}{vuln_indicator}"
|
||||||
@@ -1466,15 +1466,15 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||||||
agent_name_raw = agent_data.get("name", "Agent")
|
agent_name_raw = agent_data.get("name", "Agent")
|
||||||
|
|
||||||
status_indicators = {
|
status_indicators = {
|
||||||
"running": "🟢",
|
"running": "●",
|
||||||
"waiting": "🟡",
|
"waiting": "○",
|
||||||
"completed": "✅",
|
"completed": "◆",
|
||||||
"failed": "❌",
|
"failed": "◇",
|
||||||
"stopped": "⏹️",
|
"stopped": "■",
|
||||||
"stopping": "⏸️",
|
"stopping": "○",
|
||||||
}
|
}
|
||||||
|
|
||||||
status_icon = status_indicators.get(status, "🔵")
|
status_icon = status_indicators.get(status, "○")
|
||||||
vuln_count = self._agent_vulnerability_count(agent_id)
|
vuln_count = self._agent_vulnerability_count(agent_id)
|
||||||
vuln_indicator = f" ({vuln_count})" if vuln_count > 0 else ""
|
vuln_indicator = f" ({vuln_count})" if vuln_count > 0 else ""
|
||||||
agent_name = f"{status_icon} {agent_name_raw}{vuln_indicator}"
|
agent_name = f"{status_icon} {agent_name_raw}{vuln_indicator}"
|
||||||
@@ -1540,15 +1540,15 @@ class StrixTUIApp(App): # type: ignore[misc]
|
|||||||
status = agent_data.get("status", "running")
|
status = agent_data.get("status", "running")
|
||||||
|
|
||||||
status_indicators = {
|
status_indicators = {
|
||||||
"running": "🟢",
|
"running": "●",
|
||||||
"waiting": "🟡",
|
"waiting": "○",
|
||||||
"completed": "✅",
|
"completed": "◆",
|
||||||
"failed": "❌",
|
"failed": "◇",
|
||||||
"stopped": "⏹️",
|
"stopped": "■",
|
||||||
"stopping": "⏸️",
|
"stopping": "○",
|
||||||
}
|
}
|
||||||
|
|
||||||
status_icon = status_indicators.get(status, "🔵")
|
status_icon = status_indicators.get(status, "○")
|
||||||
vuln_count = self._agent_vulnerability_count(agent_id)
|
vuln_count = self._agent_vulnerability_count(agent_id)
|
||||||
vuln_indicator = f" ({vuln_count})" if vuln_count > 0 else ""
|
vuln_indicator = f" ({vuln_count})" if vuln_count > 0 else ""
|
||||||
agent_name = f"{status_icon} {agent_name_raw}{vuln_indicator}"
|
agent_name = f"{status_icon} {agent_name_raw}{vuln_indicator}"
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ def _build_vulnerability_stats(stats_text: Text, tracer: Any) -> None:
|
|||||||
if severity in severity_counts:
|
if severity in severity_counts:
|
||||||
severity_counts[severity] += 1
|
severity_counts[severity] += 1
|
||||||
|
|
||||||
stats_text.append("🔍 Vulnerabilities Found: ", style="bold red")
|
stats_text.append("Vulnerabilities ", style="bold red")
|
||||||
|
|
||||||
severity_parts = []
|
severity_parts = []
|
||||||
for severity in ["critical", "high", "medium", "low", "info"]:
|
for severity in ["critical", "high", "medium", "low", "info"]:
|
||||||
@@ -230,7 +230,7 @@ def _build_vulnerability_stats(stats_text: Text, tracer: Any) -> None:
|
|||||||
stats_text.append(")", style="dim white")
|
stats_text.append(")", style="dim white")
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
else:
|
else:
|
||||||
stats_text.append("🔍 Vulnerabilities Found: ", style="bold green")
|
stats_text.append("Vulnerabilities ", style="bold #22c55e")
|
||||||
stats_text.append("0", style="bold white")
|
stats_text.append("0", style="bold white")
|
||||||
stats_text.append(" (No exploitable vulnerabilities detected)", style="dim green")
|
stats_text.append(" (No exploitable vulnerabilities detected)", style="dim green")
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
@@ -240,29 +240,29 @@ def _build_llm_stats(stats_text: Text, total_stats: dict[str, Any]) -> None:
|
|||||||
"""Build LLM usage section of stats text."""
|
"""Build LLM usage section of stats text."""
|
||||||
if total_stats["requests"] > 0:
|
if total_stats["requests"] > 0:
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
stats_text.append("📥 Input Tokens: ", style="bold cyan")
|
stats_text.append("Input Tokens ", style="dim")
|
||||||
stats_text.append(format_token_count(total_stats["input_tokens"]), style="bold white")
|
stats_text.append(format_token_count(total_stats["input_tokens"]), style="white")
|
||||||
|
|
||||||
if total_stats["cached_tokens"] > 0:
|
if total_stats["cached_tokens"] > 0:
|
||||||
stats_text.append(" • ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append("⚡ Cached Tokens: ", style="bold green")
|
stats_text.append("Cached Tokens ", style="dim")
|
||||||
stats_text.append(format_token_count(total_stats["cached_tokens"]), style="bold white")
|
stats_text.append(format_token_count(total_stats["cached_tokens"]), style="#22c55e")
|
||||||
|
|
||||||
stats_text.append(" • ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append("📤 Output Tokens: ", style="bold cyan")
|
stats_text.append("Output Tokens ", style="dim")
|
||||||
stats_text.append(format_token_count(total_stats["output_tokens"]), style="bold white")
|
stats_text.append(format_token_count(total_stats["output_tokens"]), style="white")
|
||||||
|
|
||||||
if total_stats["cost"] > 0:
|
if total_stats["cost"] > 0:
|
||||||
stats_text.append(" • ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append("💰 Total Cost: ", style="bold cyan")
|
stats_text.append("Cost ", style="dim")
|
||||||
stats_text.append(f"${total_stats['cost']:.4f}", style="bold yellow")
|
stats_text.append(f"${total_stats['cost']:.4f}", style="bold #fbbf24")
|
||||||
else:
|
else:
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
stats_text.append("💰 Total Cost: ", style="bold cyan")
|
stats_text.append("Cost ", style="dim")
|
||||||
stats_text.append("$0.0000 ", style="bold yellow")
|
stats_text.append("$0.0000 ", style="#fbbf24")
|
||||||
stats_text.append("• ", style="bold white")
|
stats_text.append("· ", style="dim white")
|
||||||
stats_text.append("📊 Tokens: ", style="bold cyan")
|
stats_text.append("Tokens ", style="dim")
|
||||||
stats_text.append("0", style="bold white")
|
stats_text.append("0", style="white")
|
||||||
|
|
||||||
|
|
||||||
def build_final_stats_text(tracer: Any) -> Text:
|
def build_final_stats_text(tracer: Any) -> Text:
|
||||||
@@ -276,10 +276,12 @@ def build_final_stats_text(tracer: Any) -> Text:
|
|||||||
tool_count = tracer.get_real_tool_count()
|
tool_count = tracer.get_real_tool_count()
|
||||||
agent_count = len(tracer.agents)
|
agent_count = len(tracer.agents)
|
||||||
|
|
||||||
stats_text.append("🤖 Agents Used: ", style="bold cyan")
|
stats_text.append("Agents", style="dim")
|
||||||
|
stats_text.append(" ")
|
||||||
stats_text.append(str(agent_count), style="bold white")
|
stats_text.append(str(agent_count), style="bold white")
|
||||||
stats_text.append(" • ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append("🛠️ Tools Called: ", style="bold cyan")
|
stats_text.append("Tools", style="dim")
|
||||||
|
stats_text.append(" ")
|
||||||
stats_text.append(str(tool_count), style="bold white")
|
stats_text.append(str(tool_count), style="bold white")
|
||||||
|
|
||||||
llm_stats = tracer.get_total_llm_stats()
|
llm_stats = tracer.get_total_llm_stats()
|
||||||
@@ -296,15 +298,16 @@ def build_live_stats_text(tracer: Any, agent_config: dict[str, Any] | None = Non
|
|||||||
if agent_config:
|
if agent_config:
|
||||||
llm_config = agent_config["llm_config"]
|
llm_config = agent_config["llm_config"]
|
||||||
model = getattr(llm_config, "model_name", "Unknown")
|
model = getattr(llm_config, "model_name", "Unknown")
|
||||||
stats_text.append(f"🧠 Model: {model}")
|
stats_text.append("Model ", style="dim")
|
||||||
|
stats_text.append(model, style="white")
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
|
|
||||||
vuln_count = len(tracer.vulnerability_reports)
|
vuln_count = len(tracer.vulnerability_reports)
|
||||||
tool_count = tracer.get_real_tool_count()
|
tool_count = tracer.get_real_tool_count()
|
||||||
agent_count = len(tracer.agents)
|
agent_count = len(tracer.agents)
|
||||||
|
|
||||||
stats_text.append("🔍 Vulnerabilities: ", style="bold white")
|
stats_text.append("Vulnerabilities ", style="dim")
|
||||||
stats_text.append(f"{vuln_count}", style="dim white")
|
stats_text.append(f"{vuln_count}", style="white")
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
if vuln_count > 0:
|
if vuln_count > 0:
|
||||||
severity_counts = {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}
|
severity_counts = {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}
|
||||||
@@ -330,33 +333,32 @@ def build_live_stats_text(tracer: Any, agent_config: dict[str, Any] | None = Non
|
|||||||
|
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
|
|
||||||
stats_text.append("🤖 Agents: ", style="bold white")
|
stats_text.append("Agents ", style="dim")
|
||||||
stats_text.append(str(agent_count), style="dim white")
|
stats_text.append(str(agent_count), style="white")
|
||||||
stats_text.append(" • ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append("🛠️ Tools: ", style="bold white")
|
stats_text.append("Tools ", style="dim")
|
||||||
stats_text.append(str(tool_count), style="dim white")
|
stats_text.append(str(tool_count), style="white")
|
||||||
|
|
||||||
llm_stats = tracer.get_total_llm_stats()
|
llm_stats = tracer.get_total_llm_stats()
|
||||||
total_stats = llm_stats["total"]
|
total_stats = llm_stats["total"]
|
||||||
|
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
|
|
||||||
stats_text.append("📥 Input: ", style="bold white")
|
stats_text.append("Input Tokens ", style="dim")
|
||||||
stats_text.append(format_token_count(total_stats["input_tokens"]), style="dim white")
|
stats_text.append(format_token_count(total_stats["input_tokens"]), style="white")
|
||||||
|
|
||||||
stats_text.append(" • ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append("⚡ ", style="bold white")
|
stats_text.append("Cached Tokens ", style="dim")
|
||||||
stats_text.append("Cached: ", style="bold white")
|
stats_text.append(format_token_count(total_stats["cached_tokens"]), style="#22c55e")
|
||||||
stats_text.append(format_token_count(total_stats["cached_tokens"]), style="dim white")
|
|
||||||
|
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
|
|
||||||
stats_text.append("📤 Output: ", style="bold white")
|
stats_text.append("Output Tokens ", style="dim")
|
||||||
stats_text.append(format_token_count(total_stats["output_tokens"]), style="dim white")
|
stats_text.append(format_token_count(total_stats["output_tokens"]), style="white")
|
||||||
|
|
||||||
stats_text.append(" • ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append("💰 Cost: ", style="bold white")
|
stats_text.append("Cost ", style="dim")
|
||||||
stats_text.append(f"${total_stats['cost']:.4f}", style="dim white")
|
stats_text.append(f"${total_stats['cost']:.4f}", style="#fbbf24")
|
||||||
|
|
||||||
return stats_text
|
return stats_text
|
||||||
|
|
||||||
@@ -668,7 +670,6 @@ def clone_repository(repo_url: str, run_name: str, dest_name: str | None = None)
|
|||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
error_text = Text()
|
error_text = Text()
|
||||||
error_text.append("❌ ", style="bold red")
|
|
||||||
error_text.append("REPOSITORY CLONE FAILED", style="bold red")
|
error_text.append("REPOSITORY CLONE FAILED", style="bold red")
|
||||||
error_text.append("\n\n", style="white")
|
error_text.append("\n\n", style="white")
|
||||||
error_text.append(f"Could not clone repository: {repo_url}\n", style="white")
|
error_text.append(f"Could not clone repository: {repo_url}\n", style="white")
|
||||||
@@ -678,8 +679,8 @@ def clone_repository(repo_url: str, run_name: str, dest_name: str | None = None)
|
|||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
error_text,
|
error_text,
|
||||||
title="[bold red]🛡️ STRIX CLONE ERROR",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="red",
|
border_style="red",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -689,7 +690,6 @@ def clone_repository(repo_url: str, run_name: str, dest_name: str | None = None)
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
error_text = Text()
|
error_text = Text()
|
||||||
error_text.append("❌ ", style="bold red")
|
|
||||||
error_text.append("GIT NOT FOUND", style="bold red")
|
error_text.append("GIT NOT FOUND", style="bold red")
|
||||||
error_text.append("\n\n", style="white")
|
error_text.append("\n\n", style="white")
|
||||||
error_text.append("Git is not installed or not available in PATH.\n", style="white")
|
error_text.append("Git is not installed or not available in PATH.\n", style="white")
|
||||||
@@ -697,8 +697,8 @@ def clone_repository(repo_url: str, run_name: str, dest_name: str | None = None)
|
|||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
error_text,
|
error_text,
|
||||||
title="[bold red]🛡️ STRIX CLONE ERROR",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="red",
|
border_style="red",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
@@ -715,7 +715,6 @@ def check_docker_connection() -> Any:
|
|||||||
except DockerException:
|
except DockerException:
|
||||||
console = Console()
|
console = Console()
|
||||||
error_text = Text()
|
error_text = Text()
|
||||||
error_text.append("❌ ", style="bold red")
|
|
||||||
error_text.append("DOCKER NOT AVAILABLE", style="bold red")
|
error_text.append("DOCKER NOT AVAILABLE", style="bold red")
|
||||||
error_text.append("\n\n", style="white")
|
error_text.append("\n\n", style="white")
|
||||||
error_text.append("Cannot connect to Docker daemon.\n", style="white")
|
error_text.append("Cannot connect to Docker daemon.\n", style="white")
|
||||||
@@ -726,8 +725,8 @@ def check_docker_connection() -> Any:
|
|||||||
|
|
||||||
panel = Panel(
|
panel = Panel(
|
||||||
error_text,
|
error_text,
|
||||||
title="[bold red]🛡️ STRIX STARTUP ERROR",
|
title="[bold white]STRIX",
|
||||||
title_align="center",
|
title_align="left",
|
||||||
border_style="red",
|
border_style="red",
|
||||||
padding=(1, 2),
|
padding=(1, 2),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user