feat: add PyInstaller build for standalone binary distribution
- Add PyInstaller spec file and build script for creating standalone executables - Add install.sh for curl | sh installation from GitHub releases - Add GitHub Actions workflow for multi-platform builds (macOS, Linux, Windows) - Move sandbox-only deps (playwright, ipython, libtmux, etc.) to optional extras - Make google-cloud-aiplatform optional ([vertex] extra) to reduce binary size - Use lazy imports in tool actions to avoid loading sandbox deps at startup - Add -v/--version flag to CLI - Add website and Discord links to completion message - Binary size: ~97MB (down from ~120MB with all deps)
This commit is contained in:
78
.github/workflows/build-release.yml
vendored
Normal file
78
.github/workflows/build-release.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
name: Build & Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
target: macos-arm64
|
||||||
|
- os: macos-15-intel
|
||||||
|
target: macos-x86_64
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: linux-x86_64
|
||||||
|
- os: windows-latest
|
||||||
|
target: windows-x86_64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
|
||||||
|
- uses: snok/install-poetry@v1
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
poetry install --with dev
|
||||||
|
poetry run pyinstaller strix.spec --noconfirm
|
||||||
|
|
||||||
|
VERSION=$(poetry version -s)
|
||||||
|
mkdir -p dist/release
|
||||||
|
|
||||||
|
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")
|
||||||
|
else
|
||||||
|
cp dist/strix "dist/release/strix-${VERSION}-${{ matrix.target }}"
|
||||||
|
chmod +x "dist/release/strix-${VERSION}-${{ matrix.target }}"
|
||||||
|
tar -C dist/release -czvf "dist/release/strix-${VERSION}-${{ matrix.target }}.tar.gz" "strix-${VERSION}-${{ matrix.target }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: strix-${{ matrix.target }}
|
||||||
|
path: |
|
||||||
|
dist/release/*.tar.gz
|
||||||
|
dist/release/*.zip
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: release
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
generate_release_notes: true
|
||||||
|
files: release/*
|
||||||
@@ -158,7 +158,7 @@ RUN mkdir -p /workspace && chown -R pentester:pentester /workspace /app
|
|||||||
COPY pyproject.toml poetry.lock ./
|
COPY pyproject.toml poetry.lock ./
|
||||||
|
|
||||||
USER pentester
|
USER pentester
|
||||||
RUN poetry install --no-root --without dev
|
RUN poetry install --no-root --without dev --extras sandbox
|
||||||
RUN poetry run playwright install chromium
|
RUN poetry run playwright install chromium
|
||||||
|
|
||||||
RUN /app/venv/bin/pip install -r /home/pentester/tools/jwt_tool/requirements.txt && \
|
RUN /app/venv/bin/pip install -r /home/pentester/tools/jwt_tool/requirements.txt && \
|
||||||
|
|||||||
460
poetry.lock
generated
460
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -45,24 +45,33 @@ strix = "strix.interface.main:main"
|
|||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.12"
|
python = "^3.12"
|
||||||
fastapi = "*"
|
# Core CLI dependencies
|
||||||
uvicorn = "*"
|
|
||||||
litellm = { version = "~1.80.7", extras = ["proxy"] }
|
litellm = { version = "~1.80.7", extras = ["proxy"] }
|
||||||
tenacity = "^9.0.0"
|
tenacity = "^9.0.0"
|
||||||
numpydoc = "^1.8.0"
|
|
||||||
pydantic = {extras = ["email"], version = "^2.11.3"}
|
pydantic = {extras = ["email"], version = "^2.11.3"}
|
||||||
ipython = "^9.3.0"
|
|
||||||
openhands-aci = "^0.3.0"
|
|
||||||
playwright = "^1.48.0"
|
|
||||||
rich = "*"
|
rich = "*"
|
||||||
docker = "^7.1.0"
|
docker = "^7.1.0"
|
||||||
gql = {extras = ["requests"], version = "^3.5.3"}
|
|
||||||
textual = "^4.0.0"
|
textual = "^4.0.0"
|
||||||
xmltodict = "^0.13.0"
|
xmltodict = "^0.13.0"
|
||||||
pyte = "^0.8.1"
|
|
||||||
requests = "^2.32.0"
|
requests = "^2.32.0"
|
||||||
libtmux = "^0.46.2"
|
|
||||||
google-cloud-aiplatform = ">=1.38"
|
# Optional LLM provider dependencies
|
||||||
|
google-cloud-aiplatform = { version = ">=1.38", optional = true }
|
||||||
|
|
||||||
|
# Sandbox-only dependencies (only needed inside Docker container)
|
||||||
|
fastapi = { version = "*", optional = true }
|
||||||
|
uvicorn = { version = "*", optional = true }
|
||||||
|
ipython = { version = "^9.3.0", optional = true }
|
||||||
|
openhands-aci = { version = "^0.3.0", optional = true }
|
||||||
|
playwright = { version = "^1.48.0", optional = true }
|
||||||
|
gql = { version = "^3.5.3", extras = ["requests"], optional = true }
|
||||||
|
pyte = { version = "^0.8.1", optional = true }
|
||||||
|
libtmux = { version = "^0.46.2", optional = true }
|
||||||
|
numpydoc = { version = "^1.8.0", optional = true }
|
||||||
|
|
||||||
|
[tool.poetry.extras]
|
||||||
|
vertex = ["google-cloud-aiplatform"]
|
||||||
|
sandbox = ["fastapi", "uvicorn", "ipython", "openhands-aci", "playwright", "gql", "pyte", "libtmux", "numpydoc"]
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
# Type checking and static analysis
|
# Type checking and static analysis
|
||||||
@@ -83,6 +92,9 @@ pre-commit = "^4.2.0"
|
|||||||
black = "^25.1.0"
|
black = "^25.1.0"
|
||||||
isort = "^6.0.1"
|
isort = "^6.0.1"
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
pyinstaller = { version = "^6.17.0", python = ">=3.12,<3.15" }
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|||||||
98
scripts/build.sh
Executable file
98
scripts/build.sh
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${BLUE}🦉 Strix Build Script${NC}"
|
||||||
|
echo "================================"
|
||||||
|
|
||||||
|
OS="$(uname -s)"
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
|
||||||
|
case "$OS" in
|
||||||
|
Linux*) OS_NAME="linux";;
|
||||||
|
Darwin*) OS_NAME="macos";;
|
||||||
|
MINGW*|MSYS*|CYGWIN*) OS_NAME="windows";;
|
||||||
|
*) OS_NAME="unknown";;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64|amd64) ARCH_NAME="x86_64";;
|
||||||
|
arm64|aarch64) ARCH_NAME="arm64";;
|
||||||
|
*) ARCH_NAME="$ARCH";;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Platform:${NC} $OS_NAME-$ARCH_NAME"
|
||||||
|
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
if ! command -v poetry &> /dev/null; then
|
||||||
|
echo -e "${RED}Error: Poetry is not installed${NC}"
|
||||||
|
echo "Please install Poetry first: https://python-poetry.org/docs/#installation"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\n${BLUE}Installing dependencies...${NC}"
|
||||||
|
poetry install --with dev
|
||||||
|
|
||||||
|
VERSION=$(poetry version -s)
|
||||||
|
echo -e "${YELLOW}Version:${NC} $VERSION"
|
||||||
|
|
||||||
|
echo -e "\n${BLUE}Cleaning previous builds...${NC}"
|
||||||
|
rm -rf build/ dist/
|
||||||
|
|
||||||
|
echo -e "\n${BLUE}Building binary with PyInstaller...${NC}"
|
||||||
|
poetry run pyinstaller strix.spec --noconfirm
|
||||||
|
|
||||||
|
RELEASE_DIR="dist/release"
|
||||||
|
mkdir -p "$RELEASE_DIR"
|
||||||
|
|
||||||
|
BINARY_NAME="strix-${VERSION}-${OS_NAME}-${ARCH_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"
|
||||||
|
|
||||||
|
if command -v 7z &> /dev/null; then
|
||||||
|
7z a "$RELEASE_DIR/$ARCHIVE_NAME" "$RELEASE_DIR/$BINARY_NAME"
|
||||||
|
else
|
||||||
|
powershell -Command "Compress-Archive -Path '$RELEASE_DIR/$BINARY_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"
|
||||||
|
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"
|
||||||
|
|
||||||
|
SIZE=$(ls -lh "$RELEASE_DIR/$BINARY_NAME" | awk '{print $5}')
|
||||||
|
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}"
|
||||||
|
|
||||||
|
echo -e "\n${GREEN}Done!${NC}"
|
||||||
328
scripts/install.sh
Executable file
328
scripts/install.sh
Executable file
@@ -0,0 +1,328 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
APP=strix
|
||||||
|
REPO="usestrix/strix"
|
||||||
|
STRIX_IMAGE="ghcr.io/usestrix/strix-sandbox:0.1.10"
|
||||||
|
|
||||||
|
MUTED='\033[0;2m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
requested_version=${VERSION:-}
|
||||||
|
SKIP_DOWNLOAD=false
|
||||||
|
|
||||||
|
raw_os=$(uname -s)
|
||||||
|
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$raw_os" in
|
||||||
|
Darwin*) os="macos" ;;
|
||||||
|
Linux*) os="linux" ;;
|
||||||
|
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
arch=$(uname -m)
|
||||||
|
if [[ "$arch" == "aarch64" ]]; then
|
||||||
|
arch="arm64"
|
||||||
|
fi
|
||||||
|
if [[ "$arch" == "x86_64" ]]; then
|
||||||
|
arch="x86_64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$os" = "macos" ] && [ "$arch" = "x86_64" ]; then
|
||||||
|
rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
|
||||||
|
if [ "$rosetta_flag" = "1" ]; then
|
||||||
|
arch="arm64"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
combo="$os-$arch"
|
||||||
|
case "$combo" in
|
||||||
|
linux-x86_64|macos-x86_64|macos-arm64|windows-x86_64)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
archive_ext=".tar.gz"
|
||||||
|
if [ "$os" = "windows" ]; then
|
||||||
|
archive_ext=".zip"
|
||||||
|
fi
|
||||||
|
|
||||||
|
target="$os-$arch"
|
||||||
|
|
||||||
|
if [ "$os" = "linux" ]; then
|
||||||
|
if ! command -v tar >/dev/null 2>&1; then
|
||||||
|
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$os" = "windows" ]; then
|
||||||
|
if ! command -v unzip >/dev/null 2>&1; then
|
||||||
|
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
INSTALL_DIR=$HOME/.strix/bin
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
if [ -z "$requested_version" ]; then
|
||||||
|
specific_version=$(curl -s "https://api.github.com/repos/$REPO/releases/latest" | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
|
||||||
|
if [[ $? -ne 0 || -z "$specific_version" ]]; then
|
||||||
|
echo -e "${RED}Failed to fetch version information${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
specific_version=$requested_version
|
||||||
|
fi
|
||||||
|
|
||||||
|
filename="$APP-${specific_version}-${target}${archive_ext}"
|
||||||
|
url="https://github.com/$REPO/releases/download/v${specific_version}/$filename"
|
||||||
|
|
||||||
|
print_message() {
|
||||||
|
local level=$1
|
||||||
|
local message=$2
|
||||||
|
local color=""
|
||||||
|
case $level in
|
||||||
|
info) color="${NC}" ;;
|
||||||
|
success) color="${GREEN}" ;;
|
||||||
|
warning) color="${YELLOW}" ;;
|
||||||
|
error) color="${RED}" ;;
|
||||||
|
esac
|
||||||
|
echo -e "${color}${message}${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_existing_installation() {
|
||||||
|
local found_paths=()
|
||||||
|
while IFS= read -r -d '' path; do
|
||||||
|
found_paths+=("$path")
|
||||||
|
done < <(which -a strix 2>/dev/null | tr '\n' '\0' || true)
|
||||||
|
|
||||||
|
if [ ${#found_paths[@]} -gt 0 ]; then
|
||||||
|
for path in "${found_paths[@]}"; do
|
||||||
|
if [[ ! -e "$path" ]] || [[ "$path" == "$INSTALL_DIR/strix"* ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$path" ]]; then
|
||||||
|
echo -e "${MUTED}Found existing strix at: ${NC}$path"
|
||||||
|
|
||||||
|
if [[ "$path" == *".local/bin"* ]]; then
|
||||||
|
echo -e "${MUTED}Removing old pipx installation...${NC}"
|
||||||
|
if command -v pipx >/dev/null 2>&1; then
|
||||||
|
pipx uninstall strix-agent 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -f "$path" 2>/dev/null || true
|
||||||
|
elif [[ -L "$path" || -f "$path" ]]; then
|
||||||
|
echo -e "${MUTED}Removing old installation...${NC}"
|
||||||
|
rm -f "$path" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_version() {
|
||||||
|
check_existing_installation
|
||||||
|
|
||||||
|
if [[ -x "$INSTALL_DIR/strix" ]]; then
|
||||||
|
installed_version=$("$INSTALL_DIR/strix" --version 2>/dev/null | awk '{print $2}' || echo "")
|
||||||
|
if [[ "$installed_version" == "$specific_version" ]]; then
|
||||||
|
print_message info "${GREEN}✓ Strix ${NC}$specific_version${GREEN} already installed${NC}"
|
||||||
|
SKIP_DOWNLOAD=true
|
||||||
|
elif [[ -n "$installed_version" ]]; then
|
||||||
|
print_message info "${MUTED}Installed: ${NC}$installed_version ${MUTED}→ Upgrading to ${NC}$specific_version"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
download_and_install() {
|
||||||
|
print_message info "\n${CYAN}🦉 Installing Strix${NC} ${MUTED}version: ${NC}$specific_version"
|
||||||
|
print_message info "${MUTED}Platform: ${NC}$target\n"
|
||||||
|
|
||||||
|
local tmp_dir=$(mktemp -d)
|
||||||
|
cd "$tmp_dir"
|
||||||
|
|
||||||
|
echo -e "${MUTED}Downloading...${NC}"
|
||||||
|
curl -# -L -o "$filename" "$url"
|
||||||
|
|
||||||
|
if [ ! -f "$filename" ]; then
|
||||||
|
echo -e "${RED}Download failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${MUTED}Extracting...${NC}"
|
||||||
|
if [ "$os" = "windows" ]; then
|
||||||
|
unzip -q "$filename"
|
||||||
|
mv "strix-${specific_version}-${target}.exe" "$INSTALL_DIR/strix.exe"
|
||||||
|
else
|
||||||
|
tar -xzf "$filename"
|
||||||
|
mv "strix-${specific_version}-${target}" "$INSTALL_DIR/strix"
|
||||||
|
chmod 755 "$INSTALL_DIR/strix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd - > /dev/null
|
||||||
|
rm -rf "$tmp_dir"
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Strix installed to $INSTALL_DIR${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_docker() {
|
||||||
|
echo ""
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
echo -e "${YELLOW}⚠ Docker not found${NC}"
|
||||||
|
echo -e "${MUTED}Strix requires Docker to run the security sandbox.${NC}"
|
||||||
|
echo -e "${MUTED}Please install Docker: ${NC}https://docs.docker.com/get-docker/"
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker info >/dev/null 2>&1; then
|
||||||
|
echo -e "${YELLOW}⚠ Docker daemon not running${NC}"
|
||||||
|
echo -e "${MUTED}Please start Docker and run: ${NC}docker pull $STRIX_IMAGE"
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${MUTED}Checking for sandbox image...${NC}"
|
||||||
|
if docker image inspect "$STRIX_IMAGE" >/dev/null 2>&1; then
|
||||||
|
echo -e "${GREEN}✓ Sandbox image already available${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${MUTED}Pulling sandbox image (this may take a few minutes)...${NC}"
|
||||||
|
if docker pull "$STRIX_IMAGE"; then
|
||||||
|
echo -e "${GREEN}✓ Sandbox image pulled successfully${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ Failed to pull sandbox image${NC}"
|
||||||
|
echo -e "${MUTED}You can pull it manually later: ${NC}docker pull $STRIX_IMAGE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
add_to_path() {
|
||||||
|
local config_file=$1
|
||||||
|
local command=$2
|
||||||
|
if grep -Fxq "$command" "$config_file" 2>/dev/null; then
|
||||||
|
return 0
|
||||||
|
elif [[ -w $config_file ]]; then
|
||||||
|
echo -e "\n# strix" >> "$config_file"
|
||||||
|
echo "$command" >> "$config_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_path() {
|
||||||
|
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
|
||||||
|
current_shell=$(basename "$SHELL")
|
||||||
|
|
||||||
|
case $current_shell in
|
||||||
|
fish)
|
||||||
|
config_files="$HOME/.config/fish/config.fish"
|
||||||
|
;;
|
||||||
|
zsh)
|
||||||
|
config_files="$HOME/.zshrc $HOME/.zshenv"
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
config_files="$HOME/.bashrc $HOME/.profile"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
config_file=""
|
||||||
|
for file in $config_files; do
|
||||||
|
if [[ -f $file ]]; then
|
||||||
|
config_file=$file
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $config_file ]]; then
|
||||||
|
config_file="$HOME/.bashrc"
|
||||||
|
touch "$config_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||||||
|
case $current_shell in
|
||||||
|
fish)
|
||||||
|
add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
add_to_path "$config_file" "export PATH=\"$INSTALL_DIR:\$PATH\""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||||||
|
echo "$INSTALL_DIR" >> "$GITHUB_PATH"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_installation() {
|
||||||
|
export PATH="$INSTALL_DIR:$PATH"
|
||||||
|
|
||||||
|
local which_strix=$(which strix 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [[ "$which_strix" != "$INSTALL_DIR/strix" && "$which_strix" != "$INSTALL_DIR/strix.exe" ]]; then
|
||||||
|
if [[ -n "$which_strix" ]]; then
|
||||||
|
echo -e "${YELLOW}⚠ Found conflicting strix at: ${NC}$which_strix"
|
||||||
|
echo -e "${MUTED}Attempting to remove...${NC}"
|
||||||
|
|
||||||
|
if rm -f "$which_strix" 2>/dev/null; then
|
||||||
|
echo -e "${GREEN}✓ Removed conflicting installation${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Could not remove automatically.${NC}"
|
||||||
|
echo -e "${MUTED}Please remove manually: ${NC}rm $which_strix"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -x "$INSTALL_DIR/strix" ]]; then
|
||||||
|
local version=$("$INSTALL_DIR/strix" --version 2>/dev/null | awk '{print $2}' || echo "unknown")
|
||||||
|
echo -e "${GREEN}✓ Strix ${NC}$version${GREEN} ready${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_version
|
||||||
|
if [ "$SKIP_DOWNLOAD" = false ]; then
|
||||||
|
download_and_install
|
||||||
|
fi
|
||||||
|
setup_path
|
||||||
|
verify_installation
|
||||||
|
check_docker
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}"
|
||||||
|
echo " ███████╗████████╗██████╗ ██╗██╗ ██╗"
|
||||||
|
echo " ██╔════╝╚══██╔══╝██╔══██╗██║╚██╗██╔╝"
|
||||||
|
echo " ███████╗ ██║ ██████╔╝██║ ╚███╔╝ "
|
||||||
|
echo " ╚════██║ ██║ ██╔══██╗██║ ██╔██╗ "
|
||||||
|
echo " ███████║ ██║ ██║ ██║██║██╔╝ ██╗"
|
||||||
|
echo " ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝"
|
||||||
|
echo -e "${NC}"
|
||||||
|
echo -e "${MUTED} AI Penetration Testing Agent${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${MUTED}To get started:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${CYAN}1.${NC} Set your LLM provider:"
|
||||||
|
echo -e " ${MUTED}export STRIX_LLM='openai/gpt-5'${NC}"
|
||||||
|
echo -e " ${MUTED}export LLM_API_KEY='your-api-key'${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${CYAN}2.${NC} Run a penetration test:"
|
||||||
|
echo -e " ${MUTED}strix --target https://example.com${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${MUTED}For more information visit ${NC}https://usestrix.com"
|
||||||
|
echo -e "${MUTED}Join our community ${NC}https://discord.gg/YjKFvEZSdZ"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||||||
|
echo -e "${YELLOW}→${NC} Run ${MUTED}source ~/.$(basename $SHELL)rc${NC} or open a new terminal"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
221
strix.spec
Normal file
221
strix.spec
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
||||||
|
|
||||||
|
project_root = Path(SPECPATH)
|
||||||
|
strix_root = project_root / 'strix'
|
||||||
|
|
||||||
|
datas = []
|
||||||
|
|
||||||
|
for jinja_file in strix_root.rglob('*.jinja'):
|
||||||
|
rel_path = jinja_file.relative_to(project_root)
|
||||||
|
datas.append((str(jinja_file), str(rel_path.parent)))
|
||||||
|
|
||||||
|
for xml_file in strix_root.rglob('*.xml'):
|
||||||
|
rel_path = xml_file.relative_to(project_root)
|
||||||
|
datas.append((str(xml_file), str(rel_path.parent)))
|
||||||
|
|
||||||
|
for tcss_file in strix_root.rglob('*.tcss'):
|
||||||
|
rel_path = tcss_file.relative_to(project_root)
|
||||||
|
datas.append((str(tcss_file), str(rel_path.parent)))
|
||||||
|
|
||||||
|
datas += collect_data_files('textual')
|
||||||
|
|
||||||
|
datas += collect_data_files('tiktoken')
|
||||||
|
datas += collect_data_files('tiktoken_ext')
|
||||||
|
|
||||||
|
datas += collect_data_files('litellm')
|
||||||
|
|
||||||
|
hiddenimports = [
|
||||||
|
# Core dependencies
|
||||||
|
'litellm',
|
||||||
|
'litellm.llms',
|
||||||
|
'litellm.llms.openai',
|
||||||
|
'litellm.llms.anthropic',
|
||||||
|
'litellm.llms.vertex_ai',
|
||||||
|
'litellm.llms.bedrock',
|
||||||
|
'litellm.utils',
|
||||||
|
'litellm.caching',
|
||||||
|
|
||||||
|
# Textual TUI
|
||||||
|
'textual',
|
||||||
|
'textual.app',
|
||||||
|
'textual.widgets',
|
||||||
|
'textual.containers',
|
||||||
|
'textual.screen',
|
||||||
|
'textual.binding',
|
||||||
|
'textual.reactive',
|
||||||
|
'textual.css',
|
||||||
|
'textual._text_area_theme',
|
||||||
|
|
||||||
|
# Rich console
|
||||||
|
'rich',
|
||||||
|
'rich.console',
|
||||||
|
'rich.panel',
|
||||||
|
'rich.text',
|
||||||
|
'rich.markup',
|
||||||
|
'rich.style',
|
||||||
|
'rich.align',
|
||||||
|
'rich.live',
|
||||||
|
|
||||||
|
# Pydantic
|
||||||
|
'pydantic',
|
||||||
|
'pydantic.fields',
|
||||||
|
'pydantic_core',
|
||||||
|
'email_validator',
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
'docker',
|
||||||
|
'docker.api',
|
||||||
|
'docker.models',
|
||||||
|
'docker.errors',
|
||||||
|
|
||||||
|
# HTTP/Networking
|
||||||
|
'httpx',
|
||||||
|
'httpcore',
|
||||||
|
'requests',
|
||||||
|
'urllib3',
|
||||||
|
'certifi',
|
||||||
|
|
||||||
|
# Jinja2 templating
|
||||||
|
'jinja2',
|
||||||
|
'jinja2.ext',
|
||||||
|
'markupsafe',
|
||||||
|
|
||||||
|
# XML parsing
|
||||||
|
'xmltodict',
|
||||||
|
|
||||||
|
# Tiktoken (for token counting)
|
||||||
|
'tiktoken',
|
||||||
|
'tiktoken_ext',
|
||||||
|
'tiktoken_ext.openai_public',
|
||||||
|
|
||||||
|
# Tenacity retry
|
||||||
|
'tenacity',
|
||||||
|
|
||||||
|
# Strix modules
|
||||||
|
'strix',
|
||||||
|
'strix.interface',
|
||||||
|
'strix.interface.main',
|
||||||
|
'strix.interface.cli',
|
||||||
|
'strix.interface.tui',
|
||||||
|
'strix.interface.utils',
|
||||||
|
'strix.interface.tool_components',
|
||||||
|
'strix.agents',
|
||||||
|
'strix.agents.base_agent',
|
||||||
|
'strix.agents.state',
|
||||||
|
'strix.agents.StrixAgent',
|
||||||
|
'strix.llm',
|
||||||
|
'strix.llm.llm',
|
||||||
|
'strix.llm.config',
|
||||||
|
'strix.llm.utils',
|
||||||
|
'strix.llm.request_queue',
|
||||||
|
'strix.llm.memory_compressor',
|
||||||
|
'strix.runtime',
|
||||||
|
'strix.runtime.runtime',
|
||||||
|
'strix.runtime.docker_runtime',
|
||||||
|
'strix.telemetry',
|
||||||
|
'strix.telemetry.tracer',
|
||||||
|
'strix.tools',
|
||||||
|
'strix.tools.registry',
|
||||||
|
'strix.tools.executor',
|
||||||
|
'strix.tools.argument_parser',
|
||||||
|
'strix.prompts',
|
||||||
|
]
|
||||||
|
|
||||||
|
hiddenimports += collect_submodules('litellm')
|
||||||
|
hiddenimports += collect_submodules('textual')
|
||||||
|
hiddenimports += collect_submodules('rich')
|
||||||
|
hiddenimports += collect_submodules('pydantic')
|
||||||
|
|
||||||
|
excludes = [
|
||||||
|
# Sandbox-only packages
|
||||||
|
'playwright',
|
||||||
|
'playwright.sync_api',
|
||||||
|
'playwright.async_api',
|
||||||
|
'IPython',
|
||||||
|
'ipython',
|
||||||
|
'libtmux',
|
||||||
|
'pyte',
|
||||||
|
'openhands_aci',
|
||||||
|
'openhands-aci',
|
||||||
|
'gql',
|
||||||
|
'fastapi',
|
||||||
|
'uvicorn',
|
||||||
|
'numpydoc',
|
||||||
|
|
||||||
|
# Google Cloud / Vertex AI
|
||||||
|
'google.cloud',
|
||||||
|
'google.cloud.aiplatform',
|
||||||
|
'google.api_core',
|
||||||
|
'google.auth',
|
||||||
|
'google.oauth2',
|
||||||
|
'google.protobuf',
|
||||||
|
'grpc',
|
||||||
|
'grpcio',
|
||||||
|
'grpcio_status',
|
||||||
|
|
||||||
|
# Test frameworks
|
||||||
|
'pytest',
|
||||||
|
'pytest_asyncio',
|
||||||
|
'pytest_cov',
|
||||||
|
'pytest_mock',
|
||||||
|
|
||||||
|
# Development tools
|
||||||
|
'mypy',
|
||||||
|
'ruff',
|
||||||
|
'black',
|
||||||
|
'isort',
|
||||||
|
'pylint',
|
||||||
|
'pyright',
|
||||||
|
'bandit',
|
||||||
|
'pre_commit',
|
||||||
|
|
||||||
|
# Unnecessary for runtime
|
||||||
|
'tkinter',
|
||||||
|
'matplotlib',
|
||||||
|
'numpy',
|
||||||
|
'pandas',
|
||||||
|
'scipy',
|
||||||
|
'PIL',
|
||||||
|
'cv2',
|
||||||
|
]
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['strix/interface/main.py'],
|
||||||
|
pathex=[str(project_root)],
|
||||||
|
binaries=[],
|
||||||
|
datas=datas,
|
||||||
|
hiddenimports=hiddenimports,
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=excludes,
|
||||||
|
noarchive=False,
|
||||||
|
optimize=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
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,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
@@ -233,6 +233,15 @@ async def warm_up_llm() -> None:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_version() -> str:
|
||||||
|
try:
|
||||||
|
from importlib.metadata import version
|
||||||
|
|
||||||
|
return version("strix-agent")
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
def parse_arguments() -> argparse.Namespace:
|
def parse_arguments() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Strix Multi-Agent Cybersecurity Penetration Testing Tool",
|
description="Strix Multi-Agent Cybersecurity Penetration Testing Tool",
|
||||||
@@ -268,6 +277,13 @@ Examples:
|
|||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-v",
|
||||||
|
"--version",
|
||||||
|
action="version",
|
||||||
|
version=f"strix {get_version()}",
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-t",
|
"-t",
|
||||||
"--target",
|
"--target",
|
||||||
@@ -428,6 +444,9 @@ 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://usestrix.com[/]")
|
||||||
|
console.print("[dim]💬 Discord:[/] [cyan]https://discord.gg/YjKFvEZSdZ[/]")
|
||||||
|
console.print()
|
||||||
|
|
||||||
|
|
||||||
def pull_docker_image() -> None:
|
def pull_docker_image() -> None:
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
from typing import Any, Literal, NoReturn
|
from typing import TYPE_CHECKING, Any, Literal, NoReturn
|
||||||
|
|
||||||
from strix.tools.registry import register_tool
|
from strix.tools.registry import register_tool
|
||||||
|
|
||||||
from .tab_manager import BrowserTabManager, get_browser_tab_manager
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .tab_manager import BrowserTabManager
|
||||||
|
|
||||||
|
|
||||||
BrowserAction = Literal[
|
BrowserAction = Literal[
|
||||||
@@ -71,7 +73,7 @@ def _validate_file_path(action_name: str, file_path: str | None) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _handle_navigation_actions(
|
def _handle_navigation_actions(
|
||||||
manager: BrowserTabManager,
|
manager: "BrowserTabManager",
|
||||||
action: str,
|
action: str,
|
||||||
url: str | None = None,
|
url: str | None = None,
|
||||||
tab_id: str | None = None,
|
tab_id: str | None = None,
|
||||||
@@ -90,7 +92,7 @@ def _handle_navigation_actions(
|
|||||||
|
|
||||||
|
|
||||||
def _handle_interaction_actions(
|
def _handle_interaction_actions(
|
||||||
manager: BrowserTabManager,
|
manager: "BrowserTabManager",
|
||||||
action: str,
|
action: str,
|
||||||
coordinate: str | None = None,
|
coordinate: str | None = None,
|
||||||
text: str | None = None,
|
text: str | None = None,
|
||||||
@@ -128,7 +130,7 @@ def _raise_unknown_action(action: str) -> NoReturn:
|
|||||||
|
|
||||||
|
|
||||||
def _handle_tab_actions(
|
def _handle_tab_actions(
|
||||||
manager: BrowserTabManager,
|
manager: "BrowserTabManager",
|
||||||
action: str,
|
action: str,
|
||||||
url: str | None = None,
|
url: str | None = None,
|
||||||
tab_id: str | None = None,
|
tab_id: str | None = None,
|
||||||
@@ -149,7 +151,7 @@ def _handle_tab_actions(
|
|||||||
|
|
||||||
|
|
||||||
def _handle_utility_actions(
|
def _handle_utility_actions(
|
||||||
manager: BrowserTabManager,
|
manager: "BrowserTabManager",
|
||||||
action: str,
|
action: str,
|
||||||
duration: float | None = None,
|
duration: float | None = None,
|
||||||
js_code: str | None = None,
|
js_code: str | None = None,
|
||||||
@@ -191,6 +193,8 @@ def browser_action(
|
|||||||
file_path: str | None = None,
|
file_path: str | None = None,
|
||||||
clear: bool = False,
|
clear: bool = False,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .tab_manager import get_browser_tab_manager
|
||||||
|
|
||||||
manager = get_browser_tab_manager()
|
manager = get_browser_tab_manager()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ import re
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from openhands_aci import file_editor
|
|
||||||
from openhands_aci.utils.shell import run_shell_cmd
|
|
||||||
|
|
||||||
from strix.tools.registry import register_tool
|
from strix.tools.registry import register_tool
|
||||||
|
|
||||||
|
|
||||||
@@ -33,6 +30,8 @@ def str_replace_editor(
|
|||||||
new_str: str | None = None,
|
new_str: str | None = None,
|
||||||
insert_line: int | None = None,
|
insert_line: int | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from openhands_aci import file_editor
|
||||||
|
|
||||||
try:
|
try:
|
||||||
path_obj = Path(path)
|
path_obj = Path(path)
|
||||||
if not path_obj.is_absolute():
|
if not path_obj.is_absolute():
|
||||||
@@ -64,6 +63,8 @@ def list_files(
|
|||||||
path: str,
|
path: str,
|
||||||
recursive: bool = False,
|
recursive: bool = False,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from openhands_aci.utils.shell import run_shell_cmd
|
||||||
|
|
||||||
try:
|
try:
|
||||||
path_obj = Path(path)
|
path_obj = Path(path)
|
||||||
if not path_obj.is_absolute():
|
if not path_obj.is_absolute():
|
||||||
@@ -116,6 +117,8 @@ def search_files(
|
|||||||
regex: str,
|
regex: str,
|
||||||
file_pattern: str = "*",
|
file_pattern: str = "*",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from openhands_aci.utils.shell import run_shell_cmd
|
||||||
|
|
||||||
try:
|
try:
|
||||||
path_obj = Path(path)
|
path_obj = Path(path)
|
||||||
if not path_obj.is_absolute():
|
if not path_obj.is_absolute():
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from strix.tools.registry import register_tool
|
from strix.tools.registry import register_tool
|
||||||
|
|
||||||
from .proxy_manager import get_proxy_manager
|
|
||||||
|
|
||||||
|
|
||||||
RequestPart = Literal["request", "response"]
|
RequestPart = Literal["request", "response"]
|
||||||
|
|
||||||
@@ -27,6 +25,8 @@ def list_requests(
|
|||||||
sort_order: Literal["asc", "desc"] = "desc",
|
sort_order: Literal["asc", "desc"] = "desc",
|
||||||
scope_id: str | None = None,
|
scope_id: str | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .proxy_manager import get_proxy_manager
|
||||||
|
|
||||||
manager = get_proxy_manager()
|
manager = get_proxy_manager()
|
||||||
return manager.list_requests(
|
return manager.list_requests(
|
||||||
httpql_filter, start_page, end_page, page_size, sort_by, sort_order, scope_id
|
httpql_filter, start_page, end_page, page_size, sort_by, sort_order, scope_id
|
||||||
@@ -41,6 +41,8 @@ def view_request(
|
|||||||
page: int = 1,
|
page: int = 1,
|
||||||
page_size: int = 50,
|
page_size: int = 50,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .proxy_manager import get_proxy_manager
|
||||||
|
|
||||||
manager = get_proxy_manager()
|
manager = get_proxy_manager()
|
||||||
return manager.view_request(request_id, part, search_pattern, page, page_size)
|
return manager.view_request(request_id, part, search_pattern, page, page_size)
|
||||||
|
|
||||||
@@ -53,6 +55,8 @@ def send_request(
|
|||||||
body: str = "",
|
body: str = "",
|
||||||
timeout: int = 30,
|
timeout: int = 30,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .proxy_manager import get_proxy_manager
|
||||||
|
|
||||||
if headers is None:
|
if headers is None:
|
||||||
headers = {}
|
headers = {}
|
||||||
manager = get_proxy_manager()
|
manager = get_proxy_manager()
|
||||||
@@ -64,6 +68,8 @@ def repeat_request(
|
|||||||
request_id: str,
|
request_id: str,
|
||||||
modifications: dict[str, Any] | None = None,
|
modifications: dict[str, Any] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .proxy_manager import get_proxy_manager
|
||||||
|
|
||||||
if modifications is None:
|
if modifications is None:
|
||||||
modifications = {}
|
modifications = {}
|
||||||
manager = get_proxy_manager()
|
manager = get_proxy_manager()
|
||||||
@@ -78,6 +84,8 @@ def scope_rules(
|
|||||||
scope_id: str | None = None,
|
scope_id: str | None = None,
|
||||||
scope_name: str | None = None,
|
scope_name: str | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .proxy_manager import get_proxy_manager
|
||||||
|
|
||||||
manager = get_proxy_manager()
|
manager = get_proxy_manager()
|
||||||
return manager.scope_rules(action, allowlist, denylist, scope_id, scope_name)
|
return manager.scope_rules(action, allowlist, denylist, scope_id, scope_name)
|
||||||
|
|
||||||
@@ -89,6 +97,8 @@ def list_sitemap(
|
|||||||
depth: Literal["DIRECT", "ALL"] = "DIRECT",
|
depth: Literal["DIRECT", "ALL"] = "DIRECT",
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .proxy_manager import get_proxy_manager
|
||||||
|
|
||||||
manager = get_proxy_manager()
|
manager = get_proxy_manager()
|
||||||
return manager.list_sitemap(scope_id, parent_id, depth, page)
|
return manager.list_sitemap(scope_id, parent_id, depth, page)
|
||||||
|
|
||||||
@@ -97,5 +107,7 @@ def list_sitemap(
|
|||||||
def view_sitemap_entry(
|
def view_sitemap_entry(
|
||||||
entry_id: str,
|
entry_id: str,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .proxy_manager import get_proxy_manager
|
||||||
|
|
||||||
manager = get_proxy_manager()
|
manager = get_proxy_manager()
|
||||||
return manager.view_sitemap_entry(entry_id)
|
return manager.view_sitemap_entry(entry_id)
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from strix.tools.registry import register_tool
|
from strix.tools.registry import register_tool
|
||||||
|
|
||||||
from .python_manager import get_python_session_manager
|
|
||||||
|
|
||||||
|
|
||||||
PythonAction = Literal["new_session", "execute", "close", "list_sessions"]
|
PythonAction = Literal["new_session", "execute", "close", "list_sessions"]
|
||||||
|
|
||||||
@@ -15,6 +13,8 @@ def python_action(
|
|||||||
timeout: int = 30,
|
timeout: int = 30,
|
||||||
session_id: str | None = None,
|
session_id: str | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .python_manager import get_python_session_manager
|
||||||
|
|
||||||
def _validate_code(action_name: str, code: str | None) -> None:
|
def _validate_code(action_name: str, code: str | None) -> None:
|
||||||
if not code:
|
if not code:
|
||||||
raise ValueError(f"code parameter is required for {action_name} action")
|
raise ValueError(f"code parameter is required for {action_name} action")
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ from typing import Any
|
|||||||
|
|
||||||
from strix.tools.registry import register_tool
|
from strix.tools.registry import register_tool
|
||||||
|
|
||||||
from .terminal_manager import get_terminal_manager
|
|
||||||
|
|
||||||
|
|
||||||
@register_tool
|
@register_tool
|
||||||
def terminal_execute(
|
def terminal_execute(
|
||||||
@@ -13,6 +11,8 @@ def terminal_execute(
|
|||||||
terminal_id: str | None = None,
|
terminal_id: str | None = None,
|
||||||
no_enter: bool = False,
|
no_enter: bool = False,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
from .terminal_manager import get_terminal_manager
|
||||||
|
|
||||||
manager = get_terminal_manager()
|
manager = get_terminal_manager()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user