mirror of
https://github.com/usestrix/strix.git
synced 2026-10-29 06:32:26 +00:00
perf(cli): fast startup — lazy heavy imports + onedir standalone build
This commit is contained in:
parent
f968f8e5a7
commit
e3be6253f5
13 changed files with 206 additions and 83 deletions
19
.github/workflows/build-release.yml
vendored
19
.github/workflows/build-release.yml
vendored
|
|
@ -45,15 +45,16 @@ jobs:
|
|||
uv sync --frozen
|
||||
uv run pyinstaller strix.spec --noconfirm
|
||||
|
||||
# Onedir build: dist/strix/ is an app directory (strix + _internal/).
|
||||
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||
dist/strix.exe --version
|
||||
dist/strix/strix.exe --version
|
||||
else
|
||||
dist/strix --version
|
||||
dist/strix/strix --version
|
||||
fi
|
||||
|
||||
if [[ "${{ matrix.target }}" == "linux-arm64" ]]; then
|
||||
file dist/strix
|
||||
file dist/strix | grep -q "ARM aarch64" || {
|
||||
file dist/strix/strix
|
||||
file dist/strix/strix | grep -q "ARM aarch64" || {
|
||||
echo "::error::linux-arm64 artifact is not an ARM aarch64 binary"
|
||||
exit 1
|
||||
}
|
||||
|
|
@ -62,13 +63,15 @@ jobs:
|
|||
VERSION=$(grep '^version' pyproject.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
|
||||
mkdir -p dist/release
|
||||
|
||||
cp -R dist/strix "dist/release/strix-${VERSION}-${{ matrix.target }}"
|
||||
|
||||
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||
cp dist/strix.exe "dist/release/strix-${VERSION}-${{ matrix.target }}.exe"
|
||||
(cd dist/release && 7z a "strix-${VERSION}-${{ matrix.target }}.zip" "strix-${VERSION}-${{ matrix.target }}.exe")
|
||||
(cd dist/release && 7z a "strix-${VERSION}-${{ matrix.target }}.zip" "strix-${VERSION}-${{ matrix.target }}")
|
||||
rm -rf "dist/release/strix-${VERSION}-${{ matrix.target }}"
|
||||
else
|
||||
cp dist/strix "dist/release/strix-${VERSION}-${{ matrix.target }}"
|
||||
chmod +x "dist/release/strix-${VERSION}-${{ matrix.target }}"
|
||||
chmod +x "dist/release/strix-${VERSION}-${{ matrix.target }}/strix"
|
||||
tar -C dist/release -czvf "dist/release/strix-${VERSION}-${{ matrix.target }}.tar.gz" "strix-${VERSION}-${{ matrix.target }}"
|
||||
rm -rf "dist/release/strix-${VERSION}-${{ matrix.target }}"
|
||||
fi
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
|
|
|
|||
|
|
@ -54,45 +54,48 @@ uv run pyinstaller strix.spec --noconfirm
|
|||
RELEASE_DIR="dist/release"
|
||||
mkdir -p "$RELEASE_DIR"
|
||||
|
||||
BINARY_NAME="strix-${VERSION}-${OS_NAME}-${ARCH_NAME}"
|
||||
# Onedir build: dist/strix/ is an app directory (strix + _internal/).
|
||||
BUNDLE_NAME="strix-${VERSION}-${OS_NAME}-${ARCH_NAME}"
|
||||
|
||||
if [ "$OS_NAME" = "windows" ]; then
|
||||
EXE_NAME="strix.exe"
|
||||
else
|
||||
EXE_NAME="strix"
|
||||
fi
|
||||
|
||||
if [ ! -f "dist/strix/$EXE_NAME" ]; then
|
||||
echo -e "${RED}Build failed: Binary not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp -R "dist/strix" "$RELEASE_DIR/$BUNDLE_NAME"
|
||||
chmod +x "$RELEASE_DIR/$BUNDLE_NAME/$EXE_NAME"
|
||||
|
||||
if [ "$OS_NAME" = "windows" ]; then
|
||||
if [ ! -f "dist/strix.exe" ]; then
|
||||
echo -e "${RED}Build failed: Binary not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
BINARY_NAME="${BINARY_NAME}.exe"
|
||||
cp "dist/strix.exe" "$RELEASE_DIR/$BINARY_NAME"
|
||||
echo -e "\n${BLUE}Creating zip...${NC}"
|
||||
ARCHIVE_NAME="${BINARY_NAME%.exe}.zip"
|
||||
ARCHIVE_NAME="${BUNDLE_NAME}.zip"
|
||||
|
||||
if command -v 7z &> /dev/null; then
|
||||
7z a "$RELEASE_DIR/$ARCHIVE_NAME" "$RELEASE_DIR/$BINARY_NAME"
|
||||
(cd "$RELEASE_DIR" && 7z a "$ARCHIVE_NAME" "$BUNDLE_NAME")
|
||||
else
|
||||
powershell -Command "Compress-Archive -Path '$RELEASE_DIR/$BINARY_NAME' -DestinationPath '$RELEASE_DIR/$ARCHIVE_NAME'"
|
||||
powershell -Command "Compress-Archive -Path '$RELEASE_DIR/$BUNDLE_NAME' -DestinationPath '$RELEASE_DIR/$ARCHIVE_NAME'"
|
||||
fi
|
||||
echo -e "${GREEN}Created:${NC} $RELEASE_DIR/$ARCHIVE_NAME"
|
||||
else
|
||||
if [ ! -f "dist/strix" ]; then
|
||||
echo -e "${RED}Build failed: Binary not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
cp "dist/strix" "$RELEASE_DIR/$BINARY_NAME"
|
||||
chmod +x "$RELEASE_DIR/$BINARY_NAME"
|
||||
echo -e "\n${BLUE}Creating tarball...${NC}"
|
||||
ARCHIVE_NAME="${BINARY_NAME}.tar.gz"
|
||||
tar -czvf "$RELEASE_DIR/$ARCHIVE_NAME" -C "$RELEASE_DIR" "$BINARY_NAME"
|
||||
ARCHIVE_NAME="${BUNDLE_NAME}.tar.gz"
|
||||
tar -czvf "$RELEASE_DIR/$ARCHIVE_NAME" -C "$RELEASE_DIR" "$BUNDLE_NAME"
|
||||
echo -e "${GREEN}Created:${NC} $RELEASE_DIR/$ARCHIVE_NAME"
|
||||
fi
|
||||
|
||||
echo -e "\n${GREEN}Build successful!${NC}"
|
||||
echo "================================"
|
||||
echo -e "${YELLOW}Binary:${NC} $RELEASE_DIR/$BINARY_NAME"
|
||||
echo -e "${YELLOW}App directory:${NC} $RELEASE_DIR/$BUNDLE_NAME"
|
||||
|
||||
SIZE=$(ls -lh "$RELEASE_DIR/$BINARY_NAME" | awk '{print $5}')
|
||||
SIZE=$(du -sh "$RELEASE_DIR/$BUNDLE_NAME" | awk '{print $1}')
|
||||
echo -e "${YELLOW}Size:${NC} $SIZE"
|
||||
|
||||
echo -e "\n${BLUE}Testing binary...${NC}"
|
||||
"$RELEASE_DIR/$BINARY_NAME" --help > /dev/null 2>&1 && echo -e "${GREEN}Binary test passed!${NC}" || echo -e "${RED}Binary test failed${NC}"
|
||||
"$RELEASE_DIR/$BUNDLE_NAME/$EXE_NAME" --help > /dev/null 2>&1 && echo -e "${GREEN}Binary test passed!${NC}" || echo -e "${RED}Binary test failed${NC}"
|
||||
|
||||
echo -e "\n${GREEN}Done!${NC}"
|
||||
|
|
|
|||
|
|
@ -159,12 +159,28 @@ download_and_install() {
|
|||
fi
|
||||
|
||||
echo -e "${MUTED}Extracting...${NC}"
|
||||
local extracted="strix-${specific_version}-${target}"
|
||||
if [ "$os" = "windows" ]; then
|
||||
unzip -q "$filename"
|
||||
mv "strix-${specific_version}-${target}.exe" "$INSTALL_DIR/strix.exe"
|
||||
if [ -d "$extracted" ]; then
|
||||
# Onedir app bundle (strix.exe + _internal/).
|
||||
rm -rf "$INSTALL_DIR/_internal" "$INSTALL_DIR/strix.exe"
|
||||
cp -R "$extracted/." "$INSTALL_DIR/"
|
||||
else
|
||||
# Legacy single-file binary.
|
||||
mv "${extracted}.exe" "$INSTALL_DIR/strix.exe"
|
||||
fi
|
||||
else
|
||||
tar -xzf "$filename"
|
||||
mv "strix-${specific_version}-${target}" "$INSTALL_DIR/strix"
|
||||
if [ -d "$extracted" ]; then
|
||||
# Onedir app bundle (strix + _internal/).
|
||||
rm -rf "$INSTALL_DIR/_internal" "$INSTALL_DIR/strix"
|
||||
cp -R "$extracted/." "$INSTALL_DIR/"
|
||||
else
|
||||
# Legacy single-file binary.
|
||||
rm -rf "$INSTALL_DIR/strix"
|
||||
mv "$extracted" "$INSTALL_DIR/strix"
|
||||
fi
|
||||
chmod 755 "$INSTALL_DIR/strix"
|
||||
fi
|
||||
|
||||
|
|
|
|||
16
strix.spec
16
strix.spec
|
|
@ -276,19 +276,19 @@ a = Analysis(
|
|||
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
# Onedir build: no self-extraction at startup, so the CLI starts fast on
|
||||
# every run (onefile unpacks the full bundle to a temp dir each invocation).
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='strix',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=False,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
|
|
@ -296,3 +296,13 @@ exe = EXE(
|
|||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=False,
|
||||
upx_exclude=[],
|
||||
name='strix',
|
||||
)
|
||||
|
|
|
|||
3
strix/core/constants.py
Normal file
3
strix/core/constants.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"""Lightweight constants importable without pulling in the agent runtime."""
|
||||
|
||||
DEFAULT_MAX_TURNS = 500
|
||||
|
|
@ -24,9 +24,6 @@ if TYPE_CHECKING:
|
|||
from strix.config.settings import ReasoningEffort
|
||||
|
||||
|
||||
DEFAULT_MAX_TURNS = 500
|
||||
|
||||
|
||||
def _accepts_required_tool_choice(model_name: str | None) -> bool:
|
||||
name = (model_name or "").strip().lower()
|
||||
for prefix in ("litellm/", "any-llm/"):
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from strix.config.models import (
|
|||
uses_chat_completions_tool_schema,
|
||||
)
|
||||
from strix.core.agents import AgentCoordinator
|
||||
from strix.core.constants import DEFAULT_MAX_TURNS
|
||||
from strix.core.execution import (
|
||||
respawn_subagents,
|
||||
run_agent_loop,
|
||||
|
|
@ -33,7 +34,6 @@ from strix.core.execution import (
|
|||
)
|
||||
from strix.core.hooks import BudgetExceededError, ReportUsageHooks, recomputed_budget_flags
|
||||
from strix.core.inputs import (
|
||||
DEFAULT_MAX_TURNS,
|
||||
build_root_task,
|
||||
build_scope_context,
|
||||
make_model_settings,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,16 @@
|
|||
from .main import main
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .main import main
|
||||
|
||||
|
||||
__all__ = ["main"]
|
||||
|
||||
|
||||
def __getattr__(name: str) -> object:
|
||||
if name == "main":
|
||||
from .main import main
|
||||
|
||||
return main
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from rich.panel import Panel
|
|||
from rich.text import Text
|
||||
|
||||
from strix.config import load_settings
|
||||
from strix.core.inputs import DEFAULT_MAX_TURNS
|
||||
from strix.core.constants import DEFAULT_MAX_TURNS
|
||||
from strix.core.runner import run_strix_scan
|
||||
from strix.report.state import ReportState, set_global_report_state
|
||||
from strix.runtime import session_manager
|
||||
|
|
|
|||
|
|
@ -11,9 +11,6 @@ import sys
|
|||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
from agents.model_settings import ModelSettings
|
||||
from agents.models.interface import ModelTracing
|
||||
from docker.errors import DockerException
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.text import Text
|
||||
|
|
@ -24,17 +21,8 @@ from strix.config import (
|
|||
load_settings,
|
||||
persist_current,
|
||||
)
|
||||
from strix.config.models import (
|
||||
RECOMMENDED_MODEL_NAMES,
|
||||
StrixProvider,
|
||||
configure_sdk_model_defaults,
|
||||
is_known_openai_bare_model,
|
||||
is_recommended_or_frontier_model,
|
||||
)
|
||||
from strix.core.inputs import DEFAULT_MAX_TURNS
|
||||
from strix.core.constants import DEFAULT_MAX_TURNS
|
||||
from strix.core.paths import run_dir_for, runtime_state_dir
|
||||
from strix.interface.cli import run_cli
|
||||
from strix.interface.tui import run_tui
|
||||
from strix.interface.update_check import (
|
||||
is_binary_install,
|
||||
notify_update,
|
||||
|
|
@ -61,8 +49,6 @@ from strix.interface.utils import (
|
|||
rewrite_localhost_targets,
|
||||
validate_config_file,
|
||||
)
|
||||
from strix.report.state import get_global_report_state
|
||||
from strix.report.writer import read_run_record, write_run_record
|
||||
from strix.telemetry import posthog, scarf
|
||||
from strix.telemetry.logging import configure_dependency_logging
|
||||
|
||||
|
|
@ -313,6 +299,17 @@ async def warm_up_llm(show_model_warning: bool = True) -> None:
|
|||
console = Console()
|
||||
logger.info("Warming up LLM connection")
|
||||
|
||||
from agents.model_settings import ModelSettings
|
||||
from agents.models.interface import ModelTracing
|
||||
|
||||
from strix.config.models import (
|
||||
RECOMMENDED_MODEL_NAMES,
|
||||
StrixProvider,
|
||||
configure_sdk_model_defaults,
|
||||
is_known_openai_bare_model,
|
||||
is_recommended_or_frontier_model,
|
||||
)
|
||||
|
||||
raw_model = ""
|
||||
try:
|
||||
settings = load_settings()
|
||||
|
|
@ -776,6 +773,8 @@ Examples:
|
|||
|
||||
|
||||
def _persist_run_record(args: argparse.Namespace) -> None:
|
||||
from strix.report.writer import write_run_record
|
||||
|
||||
run_dir = run_dir_for(args.run_name)
|
||||
run_dir.mkdir(parents=True, exist_ok=True)
|
||||
run_record = {
|
||||
|
|
@ -799,6 +798,8 @@ def _persist_run_record(args: argparse.Namespace) -> None:
|
|||
|
||||
def _load_resume_state(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
|
||||
"""Populate ``args.targets_info`` and friends from a prior run's run.json."""
|
||||
from strix.report.writer import read_run_record
|
||||
|
||||
run_dir = run_dir_for(args.resume)
|
||||
state_path = run_dir / "run.json"
|
||||
if not state_path.exists():
|
||||
|
|
@ -843,6 +844,8 @@ def _load_resume_state(args: argparse.Namespace, parser: argparse.ArgumentParser
|
|||
|
||||
|
||||
def display_completion_message(args: argparse.Namespace, results_path: Path) -> None:
|
||||
from strix.report.state import get_global_report_state
|
||||
|
||||
console = Console()
|
||||
report_state = get_global_report_state()
|
||||
|
||||
|
|
@ -922,6 +925,8 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) ->
|
|||
|
||||
|
||||
def pull_docker_image() -> None:
|
||||
from docker.errors import DockerException
|
||||
|
||||
console = Console()
|
||||
client = check_docker_connection()
|
||||
|
||||
|
|
@ -1071,8 +1076,12 @@ def main() -> None:
|
|||
exit_reason = "user_exit"
|
||||
try:
|
||||
if args.non_interactive:
|
||||
from strix.interface.cli import run_cli
|
||||
|
||||
asyncio.run(run_cli(args))
|
||||
else:
|
||||
from strix.interface.tui import run_tui
|
||||
|
||||
asyncio.run(run_tui(args))
|
||||
except KeyboardInterrupt:
|
||||
exit_reason = "interrupted"
|
||||
|
|
@ -1082,6 +1091,8 @@ def main() -> None:
|
|||
scarf.error("unhandled_exception")
|
||||
raise
|
||||
finally:
|
||||
from strix.report.state import get_global_report_state
|
||||
|
||||
report_state = get_global_report_state()
|
||||
if report_state:
|
||||
status = {"interrupted": "interrupted", "error": "failed"}.get(
|
||||
|
|
@ -1097,6 +1108,8 @@ def main() -> None:
|
|||
display_completion_message(args, results_path)
|
||||
|
||||
if args.non_interactive:
|
||||
from strix.report.state import get_global_report_state
|
||||
|
||||
report_state = get_global_report_state()
|
||||
if report_state and report_state.vulnerability_reports:
|
||||
sys.exit(2)
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ from textual.widgets.tree import TreeNode
|
|||
|
||||
from strix.config import load_settings
|
||||
from strix.config.models import is_recommended_or_frontier_model
|
||||
from strix.core.constants import DEFAULT_MAX_TURNS
|
||||
from strix.core.hooks import BudgetExceededError
|
||||
from strix.core.inputs import DEFAULT_MAX_TURNS
|
||||
from strix.core.runner import run_strix_scan
|
||||
from strix.interface.tui.live_view import TuiLiveView
|
||||
from strix.interface.tui.messages import send_user_message_to_agent
|
||||
|
|
|
|||
|
|
@ -316,35 +316,99 @@ def _download_and_replace(version: str, target: str, console: Console) -> bool:
|
|||
|
||||
if is_windows:
|
||||
with zipfile.ZipFile(archive_path) as zf:
|
||||
zf.extract(binary_name, tmp_dir)
|
||||
zf.extractall(tmp_dir) # noqa: S202 # nosec B202 checksum-verified above
|
||||
else:
|
||||
with tarfile.open(archive_path, "r:gz") as tf:
|
||||
tf.extract(binary_name, tmp_dir, filter="data")
|
||||
tf.extractall(tmp_dir, filter="data") # nosec B202
|
||||
|
||||
bundle_dir = tmp_dir / f"strix-{version}-{target}"
|
||||
if bundle_dir.is_dir():
|
||||
_swap_app_bundle(bundle_dir, current_exe, is_windows=is_windows)
|
||||
return True
|
||||
|
||||
new_binary = tmp_dir / binary_name
|
||||
new_binary.chmod(new_binary.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||
|
||||
staged = current_exe.with_name(current_exe.name + ".new")
|
||||
try:
|
||||
shutil.copy2(new_binary, staged)
|
||||
if is_windows:
|
||||
# Windows can't replace a running executable in place; move it aside first.
|
||||
old = current_exe.with_name(current_exe.name + ".old")
|
||||
old.unlink(missing_ok=True)
|
||||
current_exe.rename(old)
|
||||
try:
|
||||
staged.replace(current_exe)
|
||||
except Exception:
|
||||
old.rename(current_exe)
|
||||
raise
|
||||
else:
|
||||
staged.replace(current_exe)
|
||||
except Exception:
|
||||
staged.unlink(missing_ok=True)
|
||||
raise
|
||||
if not new_binary.is_file():
|
||||
raise RuntimeError(f"unexpected archive layout in {filename}")
|
||||
_swap_single_binary(new_binary, current_exe, is_windows=is_windows)
|
||||
return True
|
||||
|
||||
|
||||
def _swap_single_binary(new_binary: Path, current_exe: Path, *, is_windows: bool) -> None:
|
||||
"""Replace a legacy single-file binary in place."""
|
||||
new_binary.chmod(new_binary.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||
|
||||
staged = current_exe.with_name(current_exe.name + ".new")
|
||||
try:
|
||||
shutil.copy2(new_binary, staged)
|
||||
if is_windows:
|
||||
# Windows can't replace a running executable in place; move it aside first.
|
||||
old = current_exe.with_name(current_exe.name + ".old")
|
||||
old.unlink(missing_ok=True)
|
||||
current_exe.rename(old)
|
||||
try:
|
||||
staged.replace(current_exe)
|
||||
except Exception:
|
||||
old.rename(current_exe)
|
||||
raise
|
||||
else:
|
||||
staged.replace(current_exe)
|
||||
except Exception:
|
||||
staged.unlink(missing_ok=True)
|
||||
raise
|
||||
|
||||
|
||||
def _swap_app_bundle(bundle_dir: Path, current_exe: Path, *, is_windows: bool) -> None:
|
||||
"""Swap an onedir app bundle (exe + ``_internal/``) into place."""
|
||||
exe_name = "strix.exe" if is_windows else "strix"
|
||||
new_exe = bundle_dir / exe_name
|
||||
new_internal = bundle_dir / "_internal"
|
||||
if not new_exe.is_file() or not new_internal.is_dir():
|
||||
raise RuntimeError("unexpected app bundle layout (missing strix or _internal)")
|
||||
new_exe.chmod(new_exe.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||
|
||||
app_dir = current_exe.parent
|
||||
internal = app_dir / "_internal"
|
||||
old_internal = app_dir / "_internal.old"
|
||||
staged_internal = app_dir / "_internal.new"
|
||||
staged_exe = current_exe.with_name(current_exe.name + ".new")
|
||||
old_exe = current_exe.with_name(current_exe.name + ".old")
|
||||
|
||||
shutil.rmtree(staged_internal, ignore_errors=True)
|
||||
shutil.rmtree(old_internal, ignore_errors=True)
|
||||
staged_exe.unlink(missing_ok=True)
|
||||
old_exe.unlink(missing_ok=True)
|
||||
|
||||
shutil.copytree(new_internal, staged_internal)
|
||||
shutil.copy2(new_exe, staged_exe)
|
||||
|
||||
internal_moved = False
|
||||
try:
|
||||
internal.rename(old_internal)
|
||||
internal_moved = True
|
||||
staged_internal.rename(internal)
|
||||
if is_windows:
|
||||
# Windows can't replace a running executable in place; move it aside first.
|
||||
current_exe.rename(old_exe)
|
||||
try:
|
||||
staged_exe.replace(current_exe)
|
||||
except Exception:
|
||||
old_exe.rename(current_exe)
|
||||
raise
|
||||
else:
|
||||
staged_exe.replace(current_exe)
|
||||
except Exception:
|
||||
if internal_moved and not internal.exists() and old_internal.exists():
|
||||
old_internal.rename(internal)
|
||||
shutil.rmtree(staged_internal, ignore_errors=True)
|
||||
staged_exe.unlink(missing_ok=True)
|
||||
raise
|
||||
|
||||
# Best effort: on Windows loaded DLLs/exe may resist deletion until restart.
|
||||
shutil.rmtree(old_internal, ignore_errors=True)
|
||||
if not is_windows:
|
||||
old_exe.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def self_update(console: Console | None = None, version: str | None = None) -> bool:
|
||||
"""Replace the running standalone binary with the latest release.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
|||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from contextvars import ContextVar
|
||||
from pathlib import Path # noqa: TC003 used at runtime by ``setup_scan_logging``
|
||||
|
|
@ -78,11 +79,12 @@ class _StdoutQuietFilter(logging.Filter):
|
|||
|
||||
def configure_dependency_logging() -> None:
|
||||
"""Quiet dependency logging/warnings that obscure Strix scan logs."""
|
||||
with contextlib.suppress(Exception):
|
||||
import litellm
|
||||
|
||||
litellm_logging = litellm._logging
|
||||
litellm_logging._disable_debugging() # type: ignore[no-untyped-call]
|
||||
# Only quiet litellm when it is already loaded: importing it here would
|
||||
# pull the whole package in (~1s+) even for paths that never use it.
|
||||
litellm = sys.modules.get("litellm")
|
||||
if litellm is not None:
|
||||
with contextlib.suppress(Exception):
|
||||
litellm._logging._disable_debugging()
|
||||
|
||||
logging.getLogger("asyncio").setLevel(logging.CRITICAL)
|
||||
logging.getLogger("asyncio").propagate = False
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue