From eb0c52b7209498d9ae7f02bf2291463579cf0c63 Mon Sep 17 00:00:00 2001 From: 0xallam Date: Sun, 7 Dec 2025 14:35:08 +0200 Subject: [PATCH] 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) --- .github/workflows/build-release.yml | 78 ++++ containers/Dockerfile | 2 +- poetry.lock | 460 +++++++++++++++------ pyproject.toml | 32 +- scripts/build.sh | 98 +++++ scripts/install.sh | 328 +++++++++++++++ strix.spec | 221 ++++++++++ strix/interface/main.py | 19 + strix/tools/browser/browser_actions.py | 16 +- strix/tools/file_edit/file_edit_actions.py | 9 +- strix/tools/proxy/proxy_actions.py | 16 +- strix/tools/python/python_actions.py | 4 +- strix/tools/terminal/terminal_actions.py | 4 +- 13 files changed, 1138 insertions(+), 149 deletions(-) create mode 100644 .github/workflows/build-release.yml create mode 100755 scripts/build.sh create mode 100755 scripts/install.sh create mode 100644 strix.spec diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..07796fe --- /dev/null +++ b/.github/workflows/build-release.yml @@ -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/* diff --git a/containers/Dockerfile b/containers/Dockerfile index d11d819..a11e7ac 100644 --- a/containers/Dockerfile +++ b/containers/Dockerfile @@ -158,7 +158,7 @@ RUN mkdir -p /workspace && chown -R pentester:pentester /workspace /app COPY pyproject.toml poetry.lock ./ 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 /app/venv/bin/pip install -r /home/pentester/tools/jwt_tool/requirements.txt && \ diff --git a/poetry.lock b/poetry.lock index b39d733..31644e8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -140,14 +140,28 @@ typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} name = "alabaster" version = "1.0.0" description = "A light, configurable Sphinx theme" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] +[[package]] +name = "altgraph" +version = "0.17.5" +description = "Python graph (network) package" +optional = false +python-versions = "*" +groups = ["dev"] +markers = "python_version < \"3.15\"" +files = [ + {file = "altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597"}, + {file = "altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7"}, +] + [[package]] name = "annotated-doc" version = "0.0.3" @@ -236,9 +250,10 @@ files = [ name = "asttokens" version = "3.0.0" description = "Annotate AST trees with source code positions" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, @@ -272,10 +287,10 @@ tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" a name = "audioop-lts" version = "0.2.2" description = "LTS Port of Python audioop" -optional = false +optional = true python-versions = ">=3.13" groups = ["main"] -markers = "python_version >= \"3.13\"" +markers = "python_version >= \"3.13\" and extra == \"sandbox\"" files = [ {file = "audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800"}, {file = "audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303"}, @@ -393,9 +408,10 @@ aio = ["azure-core[aio] (>=1.30.0)"] name = "babel" version = "2.17.0" description = "Internationalization utilities" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, @@ -445,9 +461,10 @@ yaml = ["PyYAML"] name = "beautifulsoup4" version = "4.13.4" description = "Screen-scraping library" -optional = false +optional = true python-versions = ">=3.7.0" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, @@ -468,9 +485,10 @@ lxml = ["lxml"] name = "binaryornot" version = "0.4.4" description = "Ultra-lightweight pure Python package to check if a file is binary or text." -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, @@ -568,9 +586,10 @@ crt = ["awscrt (==0.23.8)"] name = "cachetools" version = "5.5.2" description = "Extensible memoizing collections and decorators" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"vertex\" or extra == \"sandbox\"" files = [ {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, @@ -684,9 +703,10 @@ files = [ name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, @@ -800,9 +820,10 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "cobble" version = "0.1.4" description = "Create data objects" -optional = false +optional = true python-versions = ">=3.5" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "cobble-0.1.4-py3-none-any.whl", hash = "sha256:36c91b1655e599fd428e2b95fdd5f0da1ca2e9f1abb0bc871dec21a0e78a2b44"}, {file = "cobble-0.1.4.tar.gz", hash = "sha256:de38be1539992c8a06e569630717c485a5f91be2192c461ea2b220607dfa78aa"}, @@ -815,19 +836,20 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["main", "dev"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "extra == \"sandbox\" and sys_platform == \"win32\" or platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "contourpy" version = "1.3.3" description = "Python library for calculating contours of 2D quadrilateral grids" -optional = false +optional = true python-versions = ">=3.11" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1"}, {file = "contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381"}, @@ -1088,9 +1110,10 @@ test-randomorder = ["pytest-randomly"] name = "cycler" version = "0.12.1" description = "Composable style cycles" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -1104,9 +1127,10 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] name = "decorator" version = "5.2.1" description = "Decorators for Humans" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, @@ -1200,9 +1224,10 @@ websockets = ["websocket-client (>=1.3.0)"] name = "docstring-parser" version = "0.17.0" description = "Parse Python docstrings in reST, Google and Numpydoc format" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708"}, {file = "docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912"}, @@ -1217,9 +1242,10 @@ test = ["pytest"] name = "docutils" version = "0.21.2" description = "Docutils -- Python Documentation Utilities" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, @@ -1245,9 +1271,10 @@ idna = ">=2.0.0" name = "et-xmlfile" version = "2.0.0" description = "An implementation of lxml.xmlfile for the standard library" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa"}, {file = "et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54"}, @@ -1257,9 +1284,10 @@ files = [ name = "executing" version = "2.2.0" description = "Get the currently executing AST node of a frame, and other information" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, @@ -1413,9 +1441,10 @@ files = [ name = "flake8" version = "7.3.0" description = "the modular source code checker: pep8 pyflakes and co" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, @@ -1430,9 +1459,10 @@ pyflakes = ">=3.4.0,<3.5.0" name = "fonttools" version = "4.61.0" description = "Tools to manipulate font files" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "fonttools-4.61.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dc25a4a9c1225653e4431a9413d0381b1c62317b0f543bdcec24e1991f612f33"}, {file = "fonttools-4.61.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b493c32d2555e9944ec1b911ea649ff8f01a649ad9cba6c118d6798e932b3f0"}, @@ -1657,9 +1687,10 @@ tqdm = ["tqdm"] name = "gitdb" version = "4.0.12" description = "Git Object Database" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"}, {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"}, @@ -1672,9 +1703,10 @@ smmap = ">=3.0.1,<6" name = "gitpython" version = "3.1.45" description = "GitPython is a Python library used to interact with Git repositories" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77"}, {file = "gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c"}, @@ -1691,10 +1723,10 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3. name = "google-api-core" version = "2.25.2" description = "Google API client core library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.14\"" +markers = "python_version >= \"3.14\" and extra == \"vertex\"" files = [ {file = "google_api_core-2.25.2-py3-none-any.whl", hash = "sha256:e9a8f62d363dc8424a8497f4c2a47d6bcda6c16514c935629c257ab5d10210e7"}, {file = "google_api_core-2.25.2.tar.gz", hash = "sha256:1c63aa6af0d0d5e37966f157a77f9396d820fba59f9e43e9415bc3dc5baff300"}, @@ -1719,10 +1751,10 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] name = "google-api-core" version = "2.28.1" description = "Google API client core library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] -markers = "python_version < \"3.14\"" +markers = "python_version < \"3.14\" and extra == \"vertex\"" files = [ {file = "google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c"}, {file = "google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8"}, @@ -1750,9 +1782,10 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] name = "google-auth" version = "2.43.0" description = "Google Authentication Library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16"}, {file = "google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483"}, @@ -1778,9 +1811,10 @@ urllib3 = ["packaging", "urllib3"] name = "google-cloud-aiplatform" version = "1.129.0" description = "Vertex AI API client library" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "google_cloud_aiplatform-1.129.0-py2.py3-none-any.whl", hash = "sha256:b0052143a1bc05894e59fc6f910e84c504e194fadf877f84fc790b38a2267739"}, {file = "google_cloud_aiplatform-1.129.0.tar.gz", hash = "sha256:c53b9d6c529b4de2962b34425b0116f7a382a926b26e02c2196e372f9a31d196"}, @@ -1837,9 +1871,10 @@ xai = ["tensorflow (>=2.3.0,<3.0.0) ; python_version < \"3.13\""] name = "google-cloud-bigquery" version = "3.38.0" description = "Google BigQuery API client library" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "google_cloud_bigquery-3.38.0-py3-none-any.whl", hash = "sha256:e06e93ff7b245b239945ef59cb59616057598d369edac457ebf292bd61984da6"}, {file = "google_cloud_bigquery-3.38.0.tar.gz", hash = "sha256:8afcb7116f5eac849097a344eb8bfda78b7cfaae128e60e019193dd483873520"}, @@ -1870,9 +1905,10 @@ tqdm = ["tqdm (>=4.23.4,<5.0.0)"] name = "google-cloud-core" version = "2.5.0" description = "Google Cloud API client core library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc"}, {file = "google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963"}, @@ -1889,10 +1925,10 @@ grpc = ["grpcio (>=1.38.0,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75. name = "google-cloud-resource-manager" version = "1.14.2" description = "Google Cloud Resource Manager API client library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.14\"" +markers = "python_version >= \"3.14\" and extra == \"vertex\"" files = [ {file = "google_cloud_resource_manager-1.14.2-py3-none-any.whl", hash = "sha256:d0fa954dedd1d2b8e13feae9099c01b8aac515b648e612834f9942d2795a9900"}, {file = "google_cloud_resource_manager-1.14.2.tar.gz", hash = "sha256:962e2d904c550d7bac48372607904ff7bb3277e3bb4a36d80cc9a37e28e6eb74"}, @@ -1909,10 +1945,10 @@ protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4 name = "google-cloud-resource-manager" version = "1.15.0" description = "Google Cloud Resource Manager API client library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] -markers = "python_version < \"3.14\"" +markers = "python_version < \"3.14\" and extra == \"vertex\"" files = [ {file = "google_cloud_resource_manager-1.15.0-py3-none-any.whl", hash = "sha256:0ccde5db644b269ddfdf7b407a2c7b60bdbf459f8e666344a5285601d00c7f6d"}, {file = "google_cloud_resource_manager-1.15.0.tar.gz", hash = "sha256:3d0b78c3daa713f956d24e525b35e9e9a76d597c438837171304d431084cedaf"}, @@ -1933,10 +1969,10 @@ protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4 name = "google-cloud-storage" version = "3.4.1" description = "Google Cloud Storage API client library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.14\"" +markers = "extra == \"vertex\" and python_version >= \"3.14\"" files = [ {file = "google_cloud_storage-3.4.1-py3-none-any.whl", hash = "sha256:972764cc0392aa097be8f49a5354e22eb47c3f62370067fb1571ffff4a1c1189"}, {file = "google_cloud_storage-3.4.1.tar.gz", hash = "sha256:6f041a297e23a4b485fad8c305a7a6e6831855c208bcbe74d00332a909f82268"}, @@ -1958,10 +1994,10 @@ tracing = ["opentelemetry-api (>=1.1.0,<2.0.0)"] name = "google-cloud-storage" version = "3.6.0" description = "Google Cloud Storage API client library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] -markers = "python_version < \"3.14\"" +markers = "extra == \"vertex\" and python_version < \"3.14\"" files = [ {file = "google_cloud_storage-3.6.0-py3-none-any.whl", hash = "sha256:5decbdddd63b7d1fc3e266a393ad6453d2e27d172bd982b1e2f15481668db097"}, {file = "google_cloud_storage-3.6.0.tar.gz", hash = "sha256:29cc6b9a6c0fc9cdad071e375d540a5a50fbc9a7fad8300fa02fb904f6fe2ca2"}, @@ -1983,9 +2019,10 @@ tracing = ["opentelemetry-api (>=1.1.0,<2.0.0)"] name = "google-crc32c" version = "1.7.1" description = "A python wrapper of the C library 'Google CRC32C'" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76"}, {file = "google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d"}, @@ -2030,9 +2067,10 @@ testing = ["pytest"] name = "google-genai" version = "1.53.0" description = "GenAI Python SDK" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "google_genai-1.53.0-py3-none-any.whl", hash = "sha256:65a3f99e5c03c372d872cda7419f5940e723374bb12a2f3ffd5e3e56e8eb2094"}, {file = "google_genai-1.53.0.tar.gz", hash = "sha256:938a26d22f3fd32c6eeeb4276ef204ef82884e63af9842ce3eac05ceb39cbd8d"}, @@ -2056,9 +2094,10 @@ local-tokenizer = ["protobuf", "sentencepiece (>=0.2.0)"] name = "google-resumable-media" version = "2.8.0" description = "Utilities for Google Media Downloads and Resumable Uploads" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582"}, {file = "google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae"}, @@ -2075,9 +2114,10 @@ requests = ["requests (>=2.18.0,<3.0.0)"] name = "googleapis-common-protos" version = "1.72.0" description = "Common protobufs used in Google APIs" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038"}, {file = "googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5"}, @@ -2094,9 +2134,10 @@ grpc = ["grpcio (>=1.44.0,<2.0.0)"] name = "gql" version = "3.5.3" description = "GraphQL client for Python" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "gql-3.5.3-py2.py3-none-any.whl", hash = "sha256:e1fcbde2893fcafdd28114ece87ff47f1cc339a31db271fc4e1d528f5a1d4fbc"}, {file = "gql-3.5.3.tar.gz", hash = "sha256:393b8c049d58e0d2f5461b9d738a2b5f904186a40395500b4a84dd092d56e42b"}, @@ -2125,9 +2166,10 @@ websockets = ["websockets (>=10,<12)"] name = "graphql-core" version = "3.2.6" description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." -optional = false +optional = true python-versions = "<4,>=3.6" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, @@ -2137,9 +2179,10 @@ files = [ name = "greenlet" version = "3.2.4" description = "Lightweight in-process concurrent programming" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c"}, {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590"}, @@ -2217,9 +2260,10 @@ test = ["objgraph", "psutil", "setuptools"] name = "grep-ast" version = "0.9.0" description = "A tool to grep through the AST of a source file" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "grep_ast-0.9.0-py3-none-any.whl", hash = "sha256:a3973dca99f1abc026a01bbbc70e00a63860c8ff94a56182ff18b089836826d7"}, {file = "grep_ast-0.9.0.tar.gz", hash = "sha256:620a242a4493e6721338d1c9a6c234ae651f8774f4924a6dcf90f6865d4b2ee3"}, @@ -2233,9 +2277,10 @@ tree-sitter-language-pack = "*" name = "grpc-google-iam-v1" version = "0.14.3" description = "IAM API client library" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "grpc_google_iam_v1-0.14.3-py3-none-any.whl", hash = "sha256:7a7f697e017a067206a3dfef44e4c634a34d3dee135fe7d7a4613fe3e59217e6"}, {file = "grpc_google_iam_v1-0.14.3.tar.gz", hash = "sha256:879ac4ef33136c5491a6300e27575a9ec760f6cdf9a2518798c1b8977a5dc389"}, @@ -2318,9 +2363,10 @@ protobuf = ["grpcio-tools (>=1.67.1)"] name = "grpcio-status" version = "1.67.1" description = "Status proto mapping for gRPC" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "grpcio_status-1.67.1-py3-none-any.whl", hash = "sha256:16e6c085950bdacac97c779e6a502ea671232385e6e37f258884d6883392c2bd"}, {file = "grpcio_status-1.67.1.tar.gz", hash = "sha256:2bf38395e028ceeecfd8866b081f61628114b384da7d51ae064ddc8d766a5d11"}, @@ -2519,9 +2565,10 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -2567,9 +2614,10 @@ files = [ name = "ipython" version = "9.4.0" description = "IPython: Productive Interactive Computing" -optional = false +optional = true python-versions = ">=3.11" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "ipython-9.4.0-py3-none-any.whl", hash = "sha256:25850f025a446d9b359e8d296ba175a36aedd32e83ca9b5060430fe16801f066"}, {file = "ipython-9.4.0.tar.gz", hash = "sha256:c033c6d4e7914c3d9768aabe76bbe87ba1dc66a92a05db6bfa1125d81f2ee270"}, @@ -2599,9 +2647,10 @@ test-extra = ["curio", "ipykernel", "ipython[test]", "jupyter_ai", "matplotlib ( name = "ipython-pygments-lexers" version = "1.1.1" description = "Defines a variety of Pygments lexers for highlighting IPython code." -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"}, {file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"}, @@ -2642,9 +2691,10 @@ plugins = ["setuptools"] name = "jedi" version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." -optional = false +optional = true python-versions = ">=3.6" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, @@ -2816,9 +2866,10 @@ referencing = ">=0.31.0" name = "kiwisolver" version = "1.4.9" description = "A fast implementation of the Cassowary constraint solver" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b"}, {file = "kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f"}, @@ -2927,9 +2978,10 @@ files = [ name = "libcst" version = "1.5.0" description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "libcst-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23d0e07fd3ed11480f8993a1e99d58a45f914a711b14f858b8db08ae861a8a34"}, {file = "libcst-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d92c5ae2e2dc9356ad7e3d05077d9b7e5065423e45788fd86729c88729e45c6e"}, @@ -2974,9 +3026,10 @@ dev = ["Sphinx (>=5.1.1)", "black (==24.8.0)", "build (>=0.10.0)", "coverage[tom name = "libtmux" version = "0.46.2" description = "Typed library that provides an ORM wrapper for tmux, a terminal multiplexer." -optional = false +optional = true python-versions = "<4.0,>=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "libtmux-0.46.2-py3-none-any.whl", hash = "sha256:6c32dbf22bde8e5e33b2714a4295f6e838dc640f337cd4c085a044f6828c7793"}, {file = "libtmux-0.46.2.tar.gz", hash = "sha256:9a398fec5d714129c8344555d466e1a903dfc0f741ba07aabe75a8ceb25c5dda"}, @@ -3090,9 +3143,10 @@ files = [ name = "lxml" version = "6.0.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35bc626eec405f745199200ccb5c6b36f202675d204aa29bb52e27ba2b71dea8"}, {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:246b40f8a4aec341cbbf52617cad8ab7c888d944bfe12a6abd2b1f6cfb6f6082"}, @@ -3196,13 +3250,30 @@ html-clean = ["lxml_html_clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] +[[package]] +name = "macholib" +version = "1.16.4" +description = "Mach-O header analysis and editing" +optional = false +python-versions = "*" +groups = ["dev"] +markers = "python_version < \"3.15\" and sys_platform == \"darwin\"" +files = [ + {file = "macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea"}, + {file = "macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362"}, +] + +[package.dependencies] +altgraph = ">=0.17" + [[package]] name = "mammoth" version = "1.11.0" description = "Convert Word documents from docx to simple and clean HTML and Markdown" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "mammoth-1.11.0-py2.py3-none-any.whl", hash = "sha256:c077ab0d450bd7c0c6ecd529a23bf7e0fa8190c929e28998308ff4eada3f063b"}, {file = "mammoth-1.11.0.tar.gz", hash = "sha256:a0f59e442f34d5b6447f4b0999306cbf3e67aaabfa8cb516f878fb1456744637"}, @@ -3241,9 +3312,10 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] name = "markdownify" version = "1.2.0" description = "Convert HTML to markdown." -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "markdownify-1.2.0-py3-none-any.whl", hash = "sha256:48e150a1c4993d4d50f282f725c0111bd9eb25645d41fa2f543708fd44161351"}, {file = "markdownify-1.2.0.tar.gz", hash = "sha256:f6c367c54eb24ee953921804dfe6d6575c5e5b42c643955e7242034435de634c"}, @@ -3328,9 +3400,10 @@ files = [ name = "matplotlib" version = "3.10.5" description = "Python plotting package" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "matplotlib-3.10.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5d4773a6d1c106ca05cb5a5515d277a6bb96ed09e5c8fab6b7741b8fcaa62c8f"}, {file = "matplotlib-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc88af74e7ba27de6cbe6faee916024ea35d895ed3d61ef6f58c4ce97da7185a"}, @@ -3407,9 +3480,10 @@ dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setup name = "matplotlib-inline" version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, @@ -3429,6 +3503,7 @@ files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +markers = {main = "extra == \"sandbox\""} [[package]] name = "mcp" @@ -3729,9 +3804,10 @@ files = [ name = "networkx" version = "3.5" description = "Python package for creating and manipulating graphs and networks" -optional = false +optional = true python-versions = ">=3.11" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"}, {file = "networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037"}, @@ -3762,9 +3838,10 @@ files = [ name = "numpy" version = "2.3.2" description = "Fundamental package for array computing in Python" -optional = false +optional = true python-versions = ">=3.11" groups = ["main"] +markers = "extra == \"sandbox\" or extra == \"vertex\"" files = [ {file = "numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9"}, {file = "numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168"}, @@ -3846,9 +3923,10 @@ files = [ name = "numpydoc" version = "1.9.0" description = "Sphinx extension to support docstrings in Numpy format" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "numpydoc-1.9.0-py3-none-any.whl", hash = "sha256:8a2983b2d62bfd0a8c470c7caa25e7e0c3d163875cdec12a8a1034020a9d1135"}, {file = "numpydoc-1.9.0.tar.gz", hash = "sha256:5fec64908fe041acc4b3afc2a32c49aab1540cf581876f5563d68bb129e27c5b"}, @@ -3906,9 +3984,10 @@ voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] name = "openhands-aci" version = "0.3.1" description = "An Agent-Computer Interface (ACI) designed for software development agents OpenHands." -optional = false +optional = true python-versions = "<4.0,>=3.12" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "openhands_aci-0.3.1-py3-none-any.whl", hash = "sha256:d1d9d5379388bc0119c6722b8dacf63f7c747788ac5b6c26263601b2001d11c3"}, {file = "openhands_aci-0.3.1.tar.gz", hash = "sha256:125c4773b3fd2729ec0c74d005095dad21aa0f7a1e8733e5f33f3f71466f6df9"}, @@ -3952,9 +4031,10 @@ llama = ["llama-index (>=0.12.29,<0.13.0)", "llama-index-core (>=0.12.29,<0.13.0 name = "openpyxl" version = "3.1.5" description = "A Python library to read/write Excel 2010 xlsx/xlsm files" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, @@ -4072,9 +4152,10 @@ files = [ name = "pandas" version = "2.3.1" description = "Powerful data structures for data analysis, time series, and statistics" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pandas-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22c2e866f7209ebc3a8f08d75766566aae02bcc91d196935a1d9e59c7b990ac9"}, {file = "pandas-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3583d348546201aff730c8c47e49bc159833f971c2899d6097bce68b9112a4f1"}, @@ -4155,9 +4236,10 @@ xml = ["lxml (>=4.9.2)"] name = "parso" version = "0.8.4" description = "A Python Parser" -optional = false +optional = true python-versions = ">=3.6" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, @@ -4178,6 +4260,7 @@ files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +markers = {main = "extra == \"sandbox\""} [[package]] name = "pbr" @@ -4198,9 +4281,10 @@ setuptools = "*" name = "pdfminer-six" version = "20250506" description = "PDF parser and analyzer" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pdfminer_six-20250506-py3-none-any.whl", hash = "sha256:d81ad173f62e5f841b53a8ba63af1a4a355933cfc0ffabd608e568b9193909e3"}, {file = "pdfminer_six-20250506.tar.gz", hash = "sha256:b03cc8df09cf3c7aba8246deae52e0bca7ebb112a38895b5e1d4f5dd2b8ca2e7"}, @@ -4215,14 +4299,27 @@ dev = ["atheris ; python_version < \"3.12\"", "black", "mypy (==0.931)", "nox", docs = ["sphinx", "sphinx-argparse"] image = ["Pillow"] +[[package]] +name = "pefile" +version = "2024.8.26" +description = "Python PE parsing module" +optional = false +python-versions = ">=3.6.0" +groups = ["dev"] +markers = "python_version < \"3.15\" and sys_platform == \"win32\"" +files = [ + {file = "pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f"}, + {file = "pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632"}, +] + [[package]] name = "pexpect" version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." -optional = false +optional = true python-versions = "*" groups = ["main"] -markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\"" +markers = "extra == \"sandbox\" and sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -4235,9 +4332,10 @@ ptyprocess = ">=0.5" name = "pillow" version = "11.3.0" description = "Python Imaging Library (Fork)" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"}, {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"}, @@ -4377,9 +4475,10 @@ type = ["mypy (>=1.14.1)"] name = "playwright" version = "1.54.0" description = "A high-level API to automate web browsers" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "playwright-1.54.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bf3b845af744370f1bd2286c2a9536f474cc8a88dc995b72ea9a5be714c9a77d"}, {file = "playwright-1.54.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:780928b3ca2077aea90414b37e54edd0c4bbb57d1aafc42f7aa0b3fd2c2fac02"}, @@ -4478,9 +4577,10 @@ virtualenv = ">=20.10.0" name = "prompt-toolkit" version = "3.0.51" description = "Library for building powerful interactive command lines in Python" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, @@ -4601,9 +4701,10 @@ files = [ name = "proto-plus" version = "1.26.1" description = "Beautiful, Pythonic protocol buffers" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"}, {file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"}, @@ -4619,9 +4720,10 @@ testing = ["google-api-core (>=1.31.5)"] name = "protobuf" version = "5.29.5" description = "" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079"}, {file = "protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc"}, @@ -4640,10 +4742,10 @@ files = [ name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" -optional = false +optional = true python-versions = "*" groups = ["main"] -markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\"" +markers = "extra == \"sandbox\" and sys_platform != \"win32\" and sys_platform != \"emscripten\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -4653,9 +4755,10 @@ files = [ name = "pure-eval" version = "0.2.3" description = "Safely evaluate AST nodes without side effects" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, @@ -4668,9 +4771,10 @@ tests = ["pytest"] name = "puremagic" version = "1.30" description = "Pure python implementation of magic file detection" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "puremagic-1.30-py3-none-any.whl", hash = "sha256:5eeeb2dd86f335b9cfe8e205346612197af3500c6872dffebf26929f56e9d3c1"}, {file = "puremagic-1.30.tar.gz", hash = "sha256:f9ff7ac157d54e9cf3bff1addfd97233548e75e685282d84ae11e7ffee1614c9"}, @@ -4680,9 +4784,10 @@ files = [ name = "pyasn1" version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -4692,9 +4797,10 @@ files = [ name = "pyasn1-modules" version = "0.4.2" description = "A collection of ASN.1-based protocols modules" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, @@ -4707,9 +4813,10 @@ pyasn1 = ">=0.6.1,<0.7.0" name = "pycodestyle" version = "2.14.0" description = "Python style guide checker" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, @@ -4890,9 +4997,10 @@ yaml = ["pyyaml (>=6.0.1)"] name = "pydub" version = "0.25.1" description = "Manipulate audio with an simple and easy high level interface" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6"}, {file = "pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f"}, @@ -4902,9 +5010,10 @@ files = [ name = "pyee" version = "13.0.0" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498"}, {file = "pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37"}, @@ -4920,9 +5029,10 @@ dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", " name = "pyflakes" version = "3.4.0" description = "passive checker of Python programs" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, @@ -4943,6 +5053,59 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyinstaller" +version = "6.17.0" +description = "PyInstaller bundles a Python application and all its dependencies into a single package." +optional = false +python-versions = "<3.15,>=3.8" +groups = ["dev"] +markers = "python_version < \"3.15\"" +files = [ + {file = "pyinstaller-6.17.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:4e446b8030c6e5a2f712e3f82011ecf6c7ead86008357b0d23a0ec4bcde31dac"}, + {file = "pyinstaller-6.17.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:aa9fd87aaa28239c6f0d0210114029bd03f8cac316a90bab071a5092d7c85ad7"}, + {file = "pyinstaller-6.17.0-py3-none-manylinux2014_i686.whl", hash = "sha256:060b122e43e7c0b23e759a4153be34bd70914135ab955bb18a67181e0dca85a2"}, + {file = "pyinstaller-6.17.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:cd213d1a545c97dfe4a3c40e8213ff7c5127fc115c49229f27a3fa541503444b"}, + {file = "pyinstaller-6.17.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:89c0d18ba8b62c6607abd8cf2299ae5ffa5c36d8c47f39608ce8c3f357f6099f"}, + {file = "pyinstaller-6.17.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2a147b83cdebb07855bd5a663600891550062373a2ca375c58eacead33741a27"}, + {file = "pyinstaller-6.17.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:f8cfbbfa6708e54fb936df6dd6eafaf133e84efb0d2fe25b91cfeefa793c4ca4"}, + {file = "pyinstaller-6.17.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:97f4c1942f7b4cd73f9e38b49cc8f5f8a6fbb44922cb60dd3073a189b77ee1ae"}, + {file = "pyinstaller-6.17.0-py3-none-win32.whl", hash = "sha256:ce0be227a037fd4be672226db709088565484f597d6b230bceec19850fdd4c85"}, + {file = "pyinstaller-6.17.0-py3-none-win_amd64.whl", hash = "sha256:b019940dbf7a01489d6b26f9fb97db74b504e0a757010f7ad078675befc85a82"}, + {file = "pyinstaller-6.17.0-py3-none-win_arm64.whl", hash = "sha256:3c92a335e338170df7e615f75279cfeea97ade89e6dd7694943c8c185460f7b7"}, + {file = "pyinstaller-6.17.0.tar.gz", hash = "sha256:be372bd911392b88277e510940ac32a5c2a6ce4b8d00a311c78fa443f4f27313"}, +] + +[package.dependencies] +altgraph = "*" +macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} +packaging = ">=22.0" +pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} +pyinstaller-hooks-contrib = ">=2025.9" +pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} +setuptools = ">=42.0.0" + +[package.extras] +completion = ["argcomplete"] +hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2025.10" +description = "Community maintained hooks for PyInstaller" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.15\"" +files = [ + {file = "pyinstaller_hooks_contrib-2025.10-py3-none-any.whl", hash = "sha256:aa7a378518772846221f63a84d6306d9827299323243db890851474dfd1231a9"}, + {file = "pyinstaller_hooks_contrib-2025.10.tar.gz", hash = "sha256:a1a737e5c0dccf1cf6f19a25e2efd109b9fec9ddd625f97f553dac16ee884881"}, +] + +[package.dependencies] +packaging = ">=22.0" +setuptools = ">=42.0.0" + [[package]] name = "pyjwt" version = "2.10.1" @@ -5020,9 +5183,10 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyparsing" version = "3.2.3" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, @@ -5035,9 +5199,10 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "pypdf" version = "6.4.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pypdf-6.4.0-py3-none-any.whl", hash = "sha256:55ab9837ed97fd7fcc5c131d52fcc2223bc5c6b8a1488bbf7c0e27f1f0023a79"}, {file = "pypdf-6.4.0.tar.gz", hash = "sha256:4769d471f8ddc3341193ecc5d6560fa44cf8cd0abfabf21af4e195cc0c224072"}, @@ -5055,9 +5220,10 @@ image = ["Pillow (>=8.0.0)"] name = "pypdf2" version = "3.0.1" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" -optional = false +optional = true python-versions = ">=3.6" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440"}, {file = "pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928"}, @@ -5095,9 +5261,10 @@ nodejs = ["nodejs-wheel-binaries"] name = "pyte" version = "0.8.2" description = "Simple VTXXX-compatible terminal emulator." -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "pyte-0.8.2-py3-none-any.whl", hash = "sha256:85db42a35798a5aafa96ac4d8da78b090b2c933248819157fc0e6f78876a0135"}, {file = "pyte-0.8.2.tar.gz", hash = "sha256:5af970e843fa96a97149d64e170c984721f20e52227a2f57f0a54207f08f083f"}, @@ -5231,9 +5398,10 @@ files = [ name = "python-pptx" version = "1.0.2" description = "Create, read, and update PowerPoint 2007+ (.pptx) files." -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba"}, {file = "python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095"}, @@ -5288,6 +5456,19 @@ files = [ {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, ] +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +markers = "python_version < \"3.15\" and sys_platform == \"win32\"" +files = [ + {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, + {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -5355,9 +5536,10 @@ files = [ name = "rapidfuzz" version = "3.13.0" description = "rapid fuzzy string matching" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "rapidfuzz-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aafc42a1dc5e1beeba52cd83baa41372228d6d8266f6d803c16dbabbcc156255"}, {file = "rapidfuzz-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85c9a131a44a95f9cac2eb6e65531db014e09d89c4f18c7b1fa54979cb9ff1f3"}, @@ -5615,9 +5797,10 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-toolbelt" version = "1.0.0" description = "A utility belt for advanced users of python-requests" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -5649,9 +5832,10 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "roman-numerals-py" version = "3.1.0" description = "Manipulate well-formed Roman numerals" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c"}, {file = "roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d"}, @@ -5847,9 +6031,10 @@ redis = ">=3.5,<6 || >6" name = "rsa" version = "4.9.1" description = "Pure-Python RSA implementation" -optional = false +optional = true python-versions = "<4,>=3.6" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"}, {file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"}, @@ -5929,9 +6114,10 @@ type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.deve name = "shapely" version = "2.1.2" description = "Manipulation and analysis of geometric objects" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"vertex\"" files = [ {file = "shapely-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7ae48c236c0324b4e139bea88a306a04ca630f49be66741b340729d380d8f52f"}, {file = "shapely-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eba6710407f1daa8e7602c347dfc94adc02205ec27ed956346190d66579eb9ea"}, @@ -6015,9 +6201,10 @@ files = [ name = "smmap" version = "5.0.2" description = "A pure Python implementation of a sliding window memory map manager" -optional = false +optional = true python-versions = ">=3.7" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"}, {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"}, @@ -6039,9 +6226,10 @@ files = [ name = "snowballstemmer" version = "3.0.1" description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." -optional = false +optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, @@ -6075,9 +6263,10 @@ numpy = ["numpy"] name = "soupsieve" version = "2.7" description = "A modern CSS selector implementation for Beautiful Soup." -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, @@ -6087,9 +6276,10 @@ files = [ name = "speechrecognition" version = "3.14.3" description = "Library for performing speech recognition, with support for several engines and APIs, online and offline." -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "speechrecognition-3.14.3-py3-none-any.whl", hash = "sha256:1859fbb09ae23fa759200f5b0677307f1fb16e2c5c798f4259fcc41dd5399fe6"}, {file = "speechrecognition-3.14.3.tar.gz", hash = "sha256:bdd2000a9897832b33095e33adfa48580787255706092e1346d1c6c36adae0a4"}, @@ -6115,9 +6305,10 @@ whisper-local = ["openai-whisper", "soundfile"] name = "sphinx" version = "8.2.3" description = "Python documentation generator" -optional = false +optional = true python-versions = ">=3.11" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3"}, {file = "sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348"}, @@ -6151,9 +6342,10 @@ test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "pytest-xdis name = "sphinxcontrib-applehelp" version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -6168,9 +6360,10 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -6185,9 +6378,10 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -6202,9 +6396,10 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" -optional = false +optional = true python-versions = ">=3.5" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -6217,9 +6412,10 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-qthelp" version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -6234,9 +6430,10 @@ test = ["defusedxml (>=0.7.1)", "pytest"] name = "sphinxcontrib-serializinghtml" version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -6272,9 +6469,10 @@ uvicorn = ["uvicorn (>=0.34.0)"] name = "stack-data" version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, @@ -6292,10 +6490,10 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "standard-aifc" version = "3.13.0" description = "Standard library aifc redistribution. \"dead battery\"." -optional = false +optional = true python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.13\"" +markers = "python_version >= \"3.13\" and extra == \"sandbox\"" files = [ {file = "standard_aifc-3.13.0-py3-none-any.whl", hash = "sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66"}, {file = "standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43"}, @@ -6309,10 +6507,10 @@ standard-chunk = {version = "*", markers = "python_version >= \"3.13\""} name = "standard-chunk" version = "3.13.0" description = "Standard library chunk redistribution. \"dead battery\"." -optional = false +optional = true python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.13\"" +markers = "python_version >= \"3.13\" and extra == \"sandbox\"" files = [ {file = "standard_chunk-3.13.0-py3-none-any.whl", hash = "sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c"}, {file = "standard_chunk-3.13.0.tar.gz", hash = "sha256:4ac345d37d7e686d2755e01836b8d98eda0d1a3ee90375e597ae43aaf064d654"}, @@ -6508,9 +6706,10 @@ telegram = ["requests"] name = "traitlets" version = "5.14.3" description = "Traitlets Python configuration system" -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, @@ -6524,9 +6723,10 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, name = "tree-sitter" version = "0.24.0" description = "Python bindings to the Tree-sitter parsing library" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "tree-sitter-0.24.0.tar.gz", hash = "sha256:abd95af65ca2f4f7eca356343391ed669e764f37748b5352946f00f7fc78e734"}, {file = "tree_sitter-0.24.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f3f00feff1fc47a8e4863561b8da8f5e023d382dd31ed3e43cd11d4cae445445"}, @@ -6567,9 +6767,10 @@ tests = ["tree-sitter-html (>=0.23.2)", "tree-sitter-javascript (>=0.23.1)", "tr name = "tree-sitter-c-sharp" version = "0.23.1" description = "C# grammar for tree-sitter" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd"}, {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e"}, @@ -6588,9 +6789,10 @@ core = ["tree-sitter (>=0.22,<1.0)"] name = "tree-sitter-embedded-template" version = "0.23.2" description = "Embedded Template (ERB, EJS) grammar for tree-sitter" -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "tree_sitter_embedded_template-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:a505c2d2494464029d79db541cab52f6da5fb326bf3d355e69bf98b84eb89ae0"}, {file = "tree_sitter_embedded_template-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:28028b93b42cc3753261ae7ce066675d407f59de512417524f9c3ab7792b1d37"}, @@ -6609,9 +6811,10 @@ core = ["tree-sitter (>=0.22,<1.0)"] name = "tree-sitter-language-pack" version = "0.7.3" description = "Extensive Language Pack for Tree-Sitter" -optional = false +optional = true python-versions = ">=3.9.0" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "tree_sitter_language_pack-0.7.3-cp39-abi3-macosx_10_13_universal2.whl", hash = "sha256:6c4e1a48b83d8bab8d54f1d8012ae7d5a816b3972359e3fb4fe19477a6b18658"}, {file = "tree_sitter_language_pack-0.7.3-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:0be05f63cd1da06bd277570bbb6cd37c9652ddd1d2ee63ff71da20a66ce36cd8"}, @@ -6630,9 +6833,10 @@ tree-sitter-yaml = ">=0.7.0" name = "tree-sitter-yaml" version = "0.7.1" description = "YAML grammar for tree-sitter" -optional = false +optional = true python-versions = ">=3.10" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "tree_sitter_yaml-0.7.1-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:0256632914d6eb21819f21a85bab649505496ac01fac940eb08a410669346822"}, {file = "tree_sitter_yaml-0.7.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf9dd2649392e1f28a20f920f49acd9398cfb872876e338aa84562f8f868dc4d"}, @@ -6681,6 +6885,7 @@ description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main"] +markers = "platform_system == \"Windows\" or extra == \"sandbox\"" files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, @@ -6834,9 +7039,10 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess name = "wcwidth" version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -6925,9 +7131,10 @@ files = [ name = "whatthepatch" version = "1.0.7" description = "A patch parsing and application library." -optional = false +optional = true python-versions = ">=3.9" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "whatthepatch-1.0.7-py3-none-any.whl", hash = "sha256:1b6f655fd31091c001c209529dfaabbabdbad438f5de14e3951266ea0fc6e7ed"}, {file = "whatthepatch-1.0.7.tar.gz", hash = "sha256:9eefb4ebea5200408e02d413d2b4bc28daea6b78bb4b4d53431af7245f7d7edf"}, @@ -6937,9 +7144,10 @@ files = [ name = "xlrd" version = "2.0.2" description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files" -optional = false +optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "xlrd-2.0.2-py2.py3-none-any.whl", hash = "sha256:ea762c3d29f4cca48d82df517b6d89fbce4db3107f9d78713e48cd321d5c9aa9"}, {file = "xlrd-2.0.2.tar.gz", hash = "sha256:08b5e25de58f21ce71dc7db3b3b8106c1fa776f3024c54e45b45b374e89234c9"}, @@ -6954,9 +7162,10 @@ test = ["pytest", "pytest-cov"] name = "xlsxwriter" version = "3.2.5" description = "A Python module for creating Excel XLSX files." -optional = false +optional = true python-versions = ">=3.8" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "xlsxwriter-3.2.5-py3-none-any.whl", hash = "sha256:4f4824234e1eaf9d95df9a8fe974585ff91d0f5e3d3f12ace5b71e443c1c6abd"}, {file = "xlsxwriter-3.2.5.tar.gz", hash = "sha256:7e88469d607cdc920151c0ab3ce9cf1a83992d4b7bc730c5ffdd1a12115a7dbe"}, @@ -7097,9 +7306,10 @@ propcache = ">=0.2.1" name = "youtube-transcript-api" version = "0.6.2" description = "This is an python API which allows you to get the transcripts/subtitles for a given YouTube video. It also works for automatically generated subtitles, supports translating subtitles and it does not require a headless browser, like other selenium based solutions do!" -optional = false +optional = true python-versions = "*" groups = ["main"] +markers = "extra == \"sandbox\"" files = [ {file = "youtube_transcript_api-0.6.2-py3-none-any.whl", hash = "sha256:019dbf265c6a68a0591c513fff25ed5a116ce6525832aefdfb34d4df5567121c"}, {file = "youtube_transcript_api-0.6.2.tar.gz", hash = "sha256:cad223d7620633cec44f657646bffc8bbc5598bd8e70b1ad2fa8277dec305eb7"}, @@ -7128,7 +7338,11 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] +[extras] +sandbox = ["fastapi", "gql", "ipython", "libtmux", "numpydoc", "openhands-aci", "playwright", "pyte", "uvicorn"] +vertex = ["google-cloud-aiplatform"] + [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "992e46eedd083c90361c02951ae0aa6dd94c7a15915b1e21a3d1616f0d208709" +content-hash = "c33d9ef61601de836c80517ccff66cc57837baaebf22f929c766416c0b0fd818" diff --git a/pyproject.toml b/pyproject.toml index 722b9f7..dad7350 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,24 +45,33 @@ strix = "strix.interface.main:main" [tool.poetry.dependencies] python = "^3.12" -fastapi = "*" -uvicorn = "*" +# Core CLI dependencies litellm = { version = "~1.80.7", extras = ["proxy"] } tenacity = "^9.0.0" -numpydoc = "^1.8.0" pydantic = {extras = ["email"], version = "^2.11.3"} -ipython = "^9.3.0" -openhands-aci = "^0.3.0" -playwright = "^1.48.0" rich = "*" docker = "^7.1.0" -gql = {extras = ["requests"], version = "^3.5.3"} textual = "^4.0.0" xmltodict = "^0.13.0" -pyte = "^0.8.1" 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] # Type checking and static analysis @@ -83,6 +92,9 @@ pre-commit = "^4.2.0" black = "^25.1.0" isort = "^6.0.1" +# Build tools +pyinstaller = { version = "^6.17.0", python = ">=3.12,<3.15" } + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..5c8444e --- /dev/null +++ b/scripts/build.sh @@ -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}" diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..92dcf85 --- /dev/null +++ b/scripts/install.sh @@ -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 diff --git a/strix.spec b/strix.spec new file mode 100644 index 0000000..ac5fce5 --- /dev/null +++ b/strix.spec @@ -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, +) diff --git a/strix/interface/main.py b/strix/interface/main.py index f740f1b..1da6e54 100644 --- a/strix/interface/main.py +++ b/strix/interface/main.py @@ -233,6 +233,15 @@ async def warm_up_llm() -> None: 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: parser = argparse.ArgumentParser( 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( "-t", "--target", @@ -428,6 +444,9 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) -> console.print("\n") console.print(panel) 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: diff --git a/strix/tools/browser/browser_actions.py b/strix/tools/browser/browser_actions.py index ca7a26a..5726df0 100644 --- a/strix/tools/browser/browser_actions.py +++ b/strix/tools/browser/browser_actions.py @@ -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 .tab_manager import BrowserTabManager, get_browser_tab_manager + +if TYPE_CHECKING: + from .tab_manager import BrowserTabManager BrowserAction = Literal[ @@ -71,7 +73,7 @@ def _validate_file_path(action_name: str, file_path: str | None) -> None: def _handle_navigation_actions( - manager: BrowserTabManager, + manager: "BrowserTabManager", action: str, url: str | None = None, tab_id: str | None = None, @@ -90,7 +92,7 @@ def _handle_navigation_actions( def _handle_interaction_actions( - manager: BrowserTabManager, + manager: "BrowserTabManager", action: str, coordinate: str | None = None, text: str | None = None, @@ -128,7 +130,7 @@ def _raise_unknown_action(action: str) -> NoReturn: def _handle_tab_actions( - manager: BrowserTabManager, + manager: "BrowserTabManager", action: str, url: str | None = None, tab_id: str | None = None, @@ -149,7 +151,7 @@ def _handle_tab_actions( def _handle_utility_actions( - manager: BrowserTabManager, + manager: "BrowserTabManager", action: str, duration: float | None = None, js_code: str | None = None, @@ -191,6 +193,8 @@ def browser_action( file_path: str | None = None, clear: bool = False, ) -> dict[str, Any]: + from .tab_manager import get_browser_tab_manager + manager = get_browser_tab_manager() try: diff --git a/strix/tools/file_edit/file_edit_actions.py b/strix/tools/file_edit/file_edit_actions.py index e3741cc..027c6da 100644 --- a/strix/tools/file_edit/file_edit_actions.py +++ b/strix/tools/file_edit/file_edit_actions.py @@ -3,9 +3,6 @@ import re from pathlib import Path 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 @@ -33,6 +30,8 @@ def str_replace_editor( new_str: str | None = None, insert_line: int | None = None, ) -> dict[str, Any]: + from openhands_aci import file_editor + try: path_obj = Path(path) if not path_obj.is_absolute(): @@ -64,6 +63,8 @@ def list_files( path: str, recursive: bool = False, ) -> dict[str, Any]: + from openhands_aci.utils.shell import run_shell_cmd + try: path_obj = Path(path) if not path_obj.is_absolute(): @@ -116,6 +117,8 @@ def search_files( regex: str, file_pattern: str = "*", ) -> dict[str, Any]: + from openhands_aci.utils.shell import run_shell_cmd + try: path_obj = Path(path) if not path_obj.is_absolute(): diff --git a/strix/tools/proxy/proxy_actions.py b/strix/tools/proxy/proxy_actions.py index 1779c22..0a3201c 100644 --- a/strix/tools/proxy/proxy_actions.py +++ b/strix/tools/proxy/proxy_actions.py @@ -2,8 +2,6 @@ from typing import Any, Literal from strix.tools.registry import register_tool -from .proxy_manager import get_proxy_manager - RequestPart = Literal["request", "response"] @@ -27,6 +25,8 @@ def list_requests( sort_order: Literal["asc", "desc"] = "desc", scope_id: str | None = None, ) -> dict[str, Any]: + from .proxy_manager import get_proxy_manager + manager = get_proxy_manager() return manager.list_requests( 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_size: int = 50, ) -> dict[str, Any]: + from .proxy_manager import get_proxy_manager + manager = get_proxy_manager() return manager.view_request(request_id, part, search_pattern, page, page_size) @@ -53,6 +55,8 @@ def send_request( body: str = "", timeout: int = 30, ) -> dict[str, Any]: + from .proxy_manager import get_proxy_manager + if headers is None: headers = {} manager = get_proxy_manager() @@ -64,6 +68,8 @@ def repeat_request( request_id: str, modifications: dict[str, Any] | None = None, ) -> dict[str, Any]: + from .proxy_manager import get_proxy_manager + if modifications is None: modifications = {} manager = get_proxy_manager() @@ -78,6 +84,8 @@ def scope_rules( scope_id: str | None = None, scope_name: str | None = None, ) -> dict[str, Any]: + from .proxy_manager import get_proxy_manager + manager = get_proxy_manager() return manager.scope_rules(action, allowlist, denylist, scope_id, scope_name) @@ -89,6 +97,8 @@ def list_sitemap( depth: Literal["DIRECT", "ALL"] = "DIRECT", page: int = 1, ) -> dict[str, Any]: + from .proxy_manager import get_proxy_manager + manager = get_proxy_manager() return manager.list_sitemap(scope_id, parent_id, depth, page) @@ -97,5 +107,7 @@ def list_sitemap( def view_sitemap_entry( entry_id: str, ) -> dict[str, Any]: + from .proxy_manager import get_proxy_manager + manager = get_proxy_manager() return manager.view_sitemap_entry(entry_id) diff --git a/strix/tools/python/python_actions.py b/strix/tools/python/python_actions.py index c5b002b..9a575d2 100644 --- a/strix/tools/python/python_actions.py +++ b/strix/tools/python/python_actions.py @@ -2,8 +2,6 @@ from typing import Any, Literal from strix.tools.registry import register_tool -from .python_manager import get_python_session_manager - PythonAction = Literal["new_session", "execute", "close", "list_sessions"] @@ -15,6 +13,8 @@ def python_action( timeout: int = 30, session_id: str | None = None, ) -> dict[str, Any]: + from .python_manager import get_python_session_manager + def _validate_code(action_name: str, code: str | None) -> None: if not code: raise ValueError(f"code parameter is required for {action_name} action") diff --git a/strix/tools/terminal/terminal_actions.py b/strix/tools/terminal/terminal_actions.py index 1ececa4..3d3d93b 100644 --- a/strix/tools/terminal/terminal_actions.py +++ b/strix/tools/terminal/terminal_actions.py @@ -2,8 +2,6 @@ from typing import Any from strix.tools.registry import register_tool -from .terminal_manager import get_terminal_manager - @register_tool def terminal_execute( @@ -13,6 +11,8 @@ def terminal_execute( terminal_id: str | None = None, no_enter: bool = False, ) -> dict[str, Any]: + from .terminal_manager import get_terminal_manager + manager = get_terminal_manager() try: