mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
feat(security): supply-chain advisory checker + lazy-install framework + tiered install fallback (#24220)
* feat(security): supply-chain advisory checker + lazy-install framework + tiered install fallback
Three coordinated mitigations for the Mini Shai-Hulud worm hitting
mistralai 2.4.6 on PyPI (2026-05-12) and for the next single-package
compromise that follows.
# What this PR makes true
1. Users with the poisoned mistralai 2.4.6 in their venv get a loud
detection banner with copy-pasteable remediation steps the moment
they run hermes (and on every gateway startup).
2. One quarantined / yanked PyPI package can no longer silently demote
a fresh install to 'core only' — the installer keeps every other
extra and tells the user which tier landed.
3. Future opt-in backends (Mistral, ElevenLabs, Honcho, etc.) can
lazy-install on first use under a strict allowlist, instead of
eagerly pulling everything at install time.
# Detection: hermes_cli/security_advisories.py
- ADVISORIES catalog (one entry currently: shai-hulud-2026-05 for
mistralai==2.4.6). Adding the next one is a single dataclass.
- detect_compromised() uses importlib.metadata.version() — no pip
dependency, works in uv venvs that lack pip.
- Banner cache (~/.hermes/cache/advisory_banner_seen) rate-limits
the startup banner to once per 24h per advisory.
- Acks persisted to security.acked_advisories in config.yaml; never
re-banner after ack.
- Wired into:
* hermes doctor — runs first, prints full remediation block
* hermes doctor --ack <id> — dismisses an advisory
* cli.py interactive run() and single-query branches — short
stderr banner pointing at hermes doctor
* gateway/run.py startup — operator-visible warning in gateway.log
# Lazy-install framework: tools/lazy_deps.py
- LAZY_DEPS allowlist maps namespaced feature keys (tts.elevenlabs,
memory.honcho, provider.bedrock, etc.) to pip specs.
- ensure(feature) installs missing deps in the active venv via the
uv → pip → ensurepip ladder (matches tools_config._pip_install).
- Strict spec safety regex rejects URLs, file paths, shell metas,
pip flag injection, control chars — only PyPI-by-name accepted.
- Gated on security.allow_lazy_installs (default true) plus the
HERMES_DISABLE_LAZY_INSTALLS env var for restricted/audited envs.
- Migrated three backends as proof of pattern:
* tools/tts_tool.py — _import_elevenlabs() calls ensure first
* plugins/memory/honcho/client.py — get_honcho_client lazy-installs
* tts.mistral / stt.mistral entries pre-registered for when PyPI
restores mistralai
# Installer fallback tiers
scripts/install.sh, scripts/install.ps1, setup-hermes.sh:
- Centralised _BROKEN_EXTRAS list (currently: mistral). Edit one
array when a transitive breaks; users keep every other extra.
- New 'all minus known-broken' tier between [all] and the existing
PyPI-only-extras tier. Only kicks in when [all] fails resolve.
- All three tiers explicit: every fallback announces which tier
landed and prints a re-run hint when not on Tier 1.
- install.ps1 and install.sh both regenerate their tier specs from
the same _BROKEN_EXTRAS array so updates stay in sync.
Side effect: install.ps1 Tier 2 spec previously hardcoded 'mistral'
in its extra list — bug fixed by the refactor (mistral is filtered
out).
# Config
hermes_cli/config.py — DEFAULT_CONFIG.security gains:
- acked_advisories: [] (advisory IDs the user has dismissed)
- allow_lazy_installs: True (security gate for ensure())
No config version bump needed — both keys nest under existing
security: block, and load_config's deep-merge picks up DEFAULT_CONFIG
defaults for users with older configs.
# Tests
tests/hermes_cli/test_security_advisories.py — 23 tests covering:
- detect_compromised matches/non-matches, wildcard frozenset
- ack persistence, idempotence, blank rejection, config-failure path
- banner cache rate limiting + 24h re-banner + ack-stops-banner
- short_banner_lines / full_remediation_text / render_doctor_section /
gateway_log_message
- shipped catalog well-formedness invariant
tests/tools/test_lazy_deps.py — 40 tests covering:
- spec safety: 11 safe parametrized + 18 unsafe parametrized
- allowlist: unknown-feature rejection, namespace.name shape,
every shipped spec passes the safety regex
- security gating: config flag, env var, default, fail-open
- ensure() happy/sad paths: already-satisfied, install success,
pip stderr surfaced on failure, install-succeeds-but-still-missing
- is_available, feature_install_command
Combined: 63 new tests, all passing under scripts/run_tests.sh.
# Validation
- scripts/run_tests.sh tests/hermes_cli/test_security_advisories.py
tests/tools/test_lazy_deps.py → 63/63 passing
- scripts/run_tests.sh tests/hermes_cli/test_doctor.py
tests/hermes_cli/test_doctor_command_install.py
tests/tools/test_tts_mistral.py tests/tools/test_transcription_tools.py
tests/tools/test_transcription_dotenv_fallback.py → 165/165 passing
- scripts/run_tests.sh tests/hermes_cli/ tests/tools/ →
9191 passed, 8 pre-existing failures (verified on origin/main
before this change)
- bash -n on install.sh and setup-hermes.sh → OK
- py_compile on all modified .py files → OK
- End-to-end smoke test of detect_compromised + render_doctor_section
+ gateway_log_message with mocked installed version → produces
copy-pasteable remediation output
# Community
Full advisory + remediation steps:
website/docs/community/security-advisories/shai-hulud-mistralai-2026-05.md
Short-form post drafts (Discord, GitHub pinned issue, README banner):
scripts/community-announcement-shai-hulud.md
Refs: PR #24205 (mistral disabled), Socket Security advisory
<https://socket.dev/blog/mini-shai-hulud-worm-pypi>
* build(deps): pin every direct dep to ==X.Y.Z (no ranges)
Companion to the supply-chain advisory work: replace every >=/</~= range
in pyproject.toml's [project.dependencies] and [project.optional-dependencies]
with an exact ==X.Y.Z pin sourced from uv.lock.
Why: ranges allow PyPI to ship a fresh version of any direct dep at any
time without a code review on our side. With ranges, the malicious
mistralai 2.4.6 release would have been pulled by every fresh
'pip install -e .[all]' for the hours between upload and PyPI's
quarantine — exactly the install window we got hit on. Exact pins close
that window: the only way a new package version reaches a user is via
an intentional update on our end.
What the user-facing change is: nothing, behavior-wise. Every package
resolves to the same version it was already resolving to via uv.lock —
the pins just remove the resolver's freedom to pick a different one.
Cost: any user installing Hermes alongside another package that requires
a newer pin gets a resolver conflict. Acceptable for our isolated-venv
install path; documented in the new comment block.
Build-system requires line (setuptools>=61.0) is intentionally left
as a range — pinning the build backend would block fresh pip from
bootstrapping the build on architectures where that exact wheel isn't
available.
mistral extra (mistralai==2.3.0) is pinned but stays out of [all]
(per PR #24205). 'uv lock' regeneration will fail until PyPI restores
mistralai; lockfile regeneration is gated behind that, NOT on every PR.
LAZY_DEPS in tools/lazy_deps.py also moved to exact pins so the lazy-
install pathway can never resolve a different version than the one
declared in pyproject.toml.
Validation:
- Cross-checked all 77 pinned direct deps in pyproject.toml against
uv.lock — every pin matches the resolved version exactly.
- Cross-checked all LAZY_DEPS specs against uv.lock — same.
- 'uv pip install -e .[all] --dry-run' resolves 205 packages cleanly.
- tests/tools/test_lazy_deps.py + tests/hermes_cli/test_security_advisories.py
→ 63/63 passing (every shipped spec passes the safety regex).
- Doctor + TTS + transcription targeted suite → 146/146 passing.
* build(deps): hash-verify transitives via uv.lock; remove unresolvable [mistral] extra
You asked: 'what about the dependencies the dependencies rely on?' —
correctly noting that exact-pinning direct deps in pyproject.toml does
NOT cover the transitive graph. `pip install` and `uv pip install` both
re-resolve transitives fresh from PyPI at install time, so a compromised
transitive (e.g. `httpcore` if it got worm-poisoned tomorrow) would
still hit our users even with every direct dep exact-pinned.
# What this commit fixes
1. **Both real installer scripts now prefer `uv sync --locked` as Tier 0.**
uv.lock records SHA256 hashes for every transitive — a compromised
package with a different hash gets REJECTED. Falls through to the
existing `uv pip install` cascade if the lockfile is missing or
stale, with a loud warning that the fallback path does NOT
hash-verify transitives. Previously only `setup-hermes.sh` (the dev
path) used the lockfile; `scripts/install.sh` and `scripts/install.ps1`
(the paths fresh users actually run) skipped it.
2. **Removed the `[mistral]` extra entirely.** The `mistralai` PyPI
project is fully quarantined right now — every version returns 404,
so any pin we wrote was unresolvable, which broke `uv lock --check`
in CI. Restoration is documented in pyproject.toml as a 5-step
checklist (verify, re-add extra, re-enable in 4 modules, regenerate
lock, optionally re-add to [all]).
3. **Regenerated uv.lock.** 262 packages, mistralai/eval-type-backport/
jsonpath-python pruned. `uv lock --check` now passes.
# Defense-in-depth view
| Layer | Where | Protects against |
|----------------------------|-------------------|-------------------------------------------|
| Exact pins in pyproject | direct deps | new mistralai 2.4.6-style direct compromise |
| uv.lock + `--locked` install | transitive graph | transitive worm injection |
| Tier-0 hash-verified path | install.sh / .ps1 | actually USE the lockfile in fresh installs |
| `uv lock --check` CI gate | every PR | drift between pyproject and lockfile |
| `hermes_cli/security_advisories.py` | runtime | cleanup for users who already got hit |
The exact pinning + hash verification together close the supply-chain
gap. Without the lockfile path, exact pins alone are theater.
# Validation
- `uv lock --check` → passes (262 packages resolved, no drift).
- `bash -n` on install.sh + setup-hermes.sh → OK.
- 209/209 tests passing across new + adjacent test files
(test_lazy_deps.py, test_security_advisories.py, test_doctor.py,
test_tts_mistral.py, test_transcription_tools.py).
- TOML parse OK.
* chore: remove community announcement drafts (PR body covers it)
* build(deps): lazy-install every opt-in backend (anthropic, search, terminal, platforms, dashboard)
Extends the lazy-install framework to cover everything that's not used by
every hermes session. Base install drops from ~60 packages to 45.
Moved out of core dependencies = []:
- anthropic (only when provider=anthropic native, not via aggregators)
- exa-py, firecrawl-py, parallel-web (search backends; only when picked)
- fal-client (image gen; only when picked)
- edge-tts (default TTS but still optional)
New extras in pyproject.toml: [anthropic] [exa] [firecrawl] [parallel-web]
[fal] [edge-tts]. All added to [all].
New LAZY_DEPS entries: provider.anthropic, search.{exa,firecrawl,parallel},
tts.edge, image.fal, memory.hindsight, platform.{telegram,discord,matrix},
terminal.{modal,daytona,vercel}, tool.dashboard.
Each import site now calls ensure() before importing the SDK. Where the
module had a top-level try/except (telegram, discord, fastapi), the
graceful-fallback pattern was extended to lazy-install on first
check_*_requirements() call and re-bind module globals.
Updated test_windows_native_support.py tzdata check from snapshot
(>=2023.3 literal) to invariant (any version + win32 marker).
Validation:
- Base install: 45 packages (was ~60); 6 newly-extracted packages absent
- uv lock --check: passes (262 packages, no drift)
- 209/209 lazy_deps + advisory + doctor + tts/transcription tests passing
- py_compile clean on all 12 modified modules
This commit is contained in:
parent
99ad2d1372
commit
c1eb2dcda7
28 changed files with 2433 additions and 243 deletions
164
pyproject.toml
164
pyproject.toml
|
|
@ -11,84 +11,124 @@ requires-python = ">=3.11"
|
|||
authors = [{ name = "Nous Research" }]
|
||||
license = { text = "MIT" }
|
||||
dependencies = [
|
||||
# Core — pinned to known-good ranges to limit supply chain attack surface
|
||||
"openai>=2.21.0,<3",
|
||||
"anthropic>=0.39.0,<1",
|
||||
"python-dotenv>=1.2.1,<2",
|
||||
"fire>=0.7.1,<1",
|
||||
"httpx[socks]>=0.28.1,<1",
|
||||
"rich>=14.3.3,<15",
|
||||
"tenacity>=9.1.4,<10",
|
||||
"pyyaml>=6.0.2,<7",
|
||||
"ruamel.yaml>=0.18.16,<0.19",
|
||||
"requests>=2.33.0,<3", # CVE-2026-25645
|
||||
"jinja2>=3.1.5,<4",
|
||||
"pydantic>=2.12.5,<3",
|
||||
# Core — every direct dep is exact-pinned to ==X.Y.Z (no ranges).
|
||||
# Rationale: ranges allow PyPI to ship a fresh version of a transitive
|
||||
# at any time without a code review on our side. Exact pins mean the
|
||||
# only way a new package version reaches a user is via an intentional
|
||||
# update on our end (bump the pin in this file, regenerate uv.lock).
|
||||
# This was tightened on 2026-05-12 in response to the Mini Shai-Hulud
|
||||
# worm hitting mistralai 2.4.6 on PyPI; if that release had been
|
||||
# captured by `mistralai>=2.3.0,<3` rather than an exact pin, every
|
||||
# install in the hours before the quarantine would have pulled it.
|
||||
# See website/docs/community/security-advisories/shai-hulud-mistralai-2026-05.md.
|
||||
#
|
||||
# When updating: bump the version below AND regenerate uv.lock with
|
||||
# `uv lock` so the transitive resolution stays consistent. Don't
|
||||
# introduce ranges back without a written justification.
|
||||
#
|
||||
# Scope rule: only packages used by EVERY hermes session belong here.
|
||||
# Anything that's provider-specific (`anthropic`, `firecrawl-py`,
|
||||
# `exa-py`, `fal-client`, `edge-tts`, `parallel-web`) belongs in an
|
||||
# extra and gets lazy-installed via `tools/lazy_deps.py` when the
|
||||
# user picks that backend. Smaller `dependencies` = smaller blast
|
||||
# radius for the next supply-chain attack.
|
||||
"openai==2.24.0",
|
||||
"python-dotenv==1.2.1",
|
||||
"fire==0.7.1",
|
||||
"httpx[socks]==0.28.1",
|
||||
"rich==14.3.3",
|
||||
"tenacity==9.1.4",
|
||||
"pyyaml==6.0.3",
|
||||
"ruamel.yaml==0.18.17",
|
||||
"requests==2.33.0", # CVE-2026-25645
|
||||
"jinja2==3.1.6",
|
||||
"pydantic==2.12.5",
|
||||
# Interactive CLI (prompt_toolkit is used directly by cli.py)
|
||||
"prompt_toolkit>=3.0.52,<4",
|
||||
# Tools
|
||||
"exa-py>=2.9.0,<3",
|
||||
"firecrawl-py>=4.16.0,<5",
|
||||
"parallel-web>=0.4.2,<1",
|
||||
"fal-client>=0.13.1,<1",
|
||||
"prompt_toolkit==3.0.52",
|
||||
# Cron scheduler (built-in feature — scheduled cron/interval jobs use croniter).
|
||||
"croniter>=6.0.0,<7",
|
||||
# Text-to-speech (Edge TTS is free, no API key needed)
|
||||
"edge-tts>=7.2.7,<8",
|
||||
"croniter==6.0.0",
|
||||
# Skills Hub (GitHub App JWT auth — optional, only needed for bot identity)
|
||||
"PyJWT[crypto]>=2.12.0,<3", # CVE-2026-32597
|
||||
"PyJWT[crypto]==2.12.1", # CVE-2026-32597
|
||||
# Windows has no IANA tzdata shipped with the OS, so Python's ``zoneinfo``
|
||||
# (PEP 615) raises ``ZoneInfoNotFoundError`` for every non-UTC timezone
|
||||
# out of the box. ``tzdata`` ships the Olson database as a data package
|
||||
# Python resolves automatically. No-op on Linux/macOS (which have
|
||||
# /usr/share/zoneinfo). Credits: PR #13182 (@sprmn24).
|
||||
"tzdata>=2023.3; sys_platform == 'win32'",
|
||||
"tzdata==2025.3; sys_platform == 'win32'",
|
||||
# Cross-platform process / PID management. `psutil` is the canonical
|
||||
# answer for "is this PID alive" and process-tree walking across Linux,
|
||||
# macOS and Windows. It replaces POSIX-only idioms like `os.kill(pid, 0)`
|
||||
# (which is a silent killer on Windows — see CONTRIBUTING.md) and
|
||||
# `os.killpg` (which doesn't exist on Windows).
|
||||
"psutil>=5.9.0,<8",
|
||||
"psutil==7.2.2",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
modal = ["modal>=1.0.0,<2"]
|
||||
daytona = ["daytona>=0.148.0,<1"]
|
||||
vercel = ["vercel>=0.5.7,<0.6.0"]
|
||||
hindsight = ["hindsight-client>=0.4.22"]
|
||||
dev = ["debugpy>=1.8.0,<2", "pytest>=9.0.2,<10", "pytest-asyncio>=1.3.0,<2", "pytest-xdist>=3.0,<4", "pytest-split>=0.9,<1", "mcp>=1.2.0,<2", "ty>=0.0.1a29,<0.0.22", "ruff"]
|
||||
messaging = ["python-telegram-bot[webhooks]>=22.6,<23", "discord.py[voice]>=2.7.1,<3", "aiohttp>=3.13.3,<4", "slack-bolt>=1.18.0,<2", "slack-sdk>=3.27.0,<4", "qrcode>=7.0,<8"]
|
||||
# Native Anthropic provider — only needed when provider=anthropic (not via
|
||||
# OpenRouter or other aggregators).
|
||||
anthropic = ["anthropic==0.86.0"]
|
||||
# Web search backends — each only loaded when the user picks it as their
|
||||
# search provider (configured via `hermes tools` or config.yaml).
|
||||
exa = ["exa-py==2.10.2"]
|
||||
firecrawl = ["firecrawl-py==4.17.0"]
|
||||
parallel-web = ["parallel-web==0.4.2"]
|
||||
# Image generation backends
|
||||
fal = ["fal-client==0.13.1"]
|
||||
# Edge TTS — default TTS provider but still optional (users can pick
|
||||
# ElevenLabs / OpenAI / MiniMax instead).
|
||||
edge-tts = ["edge-tts==7.2.7"]
|
||||
modal = ["modal==1.3.4"]
|
||||
daytona = ["daytona==0.155.0"]
|
||||
vercel = ["vercel==0.5.7"]
|
||||
hindsight = ["hindsight-client==0.6.1"]
|
||||
dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "pytest-xdist==3.8.0", "pytest-split==0.11.0", "mcp==1.26.0", "ty==0.0.21", "ruff==0.15.10"]
|
||||
messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.3", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"]
|
||||
cron = [] # croniter is now a core dependency; this extra kept for back-compat
|
||||
slack = ["slack-bolt>=1.18.0,<2", "slack-sdk>=3.27.0,<4"]
|
||||
matrix = ["mautrix[encryption]>=0.20,<1", "Markdown>=3.6,<4", "aiosqlite>=0.20", "asyncpg>=0.29", "aiohttp-socks>=0.10,<1"]
|
||||
cli = ["simple-term-menu>=1.0,<2"]
|
||||
tts-premium = ["elevenlabs>=1.0,<2"]
|
||||
slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1"]
|
||||
matrix = ["mautrix[encryption]==0.21.0", "Markdown==3.10.2", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"]
|
||||
cli = ["simple-term-menu==1.6.6"]
|
||||
tts-premium = ["elevenlabs==1.59.0"]
|
||||
voice = [
|
||||
# Local STT pulls in wheel-only transitive deps (ctranslate2, onnxruntime),
|
||||
# so keep it out of the base install for source-build packagers like Homebrew.
|
||||
"faster-whisper>=1.0.0,<2",
|
||||
"sounddevice>=0.4.6,<1",
|
||||
"numpy>=1.24.0,<3",
|
||||
"faster-whisper==1.2.1",
|
||||
"sounddevice==0.5.5",
|
||||
"numpy==2.4.3",
|
||||
]
|
||||
pty = [
|
||||
"ptyprocess>=0.7.0,<1; sys_platform != 'win32'",
|
||||
"pywinpty>=2.0.0,<3; sys_platform == 'win32'",
|
||||
"ptyprocess==0.7.0; sys_platform != 'win32'",
|
||||
"pywinpty==2.0.15; sys_platform == 'win32'",
|
||||
]
|
||||
honcho = ["honcho-ai>=2.0.1,<3"]
|
||||
mcp = ["mcp>=1.2.0,<2"]
|
||||
homeassistant = ["aiohttp>=3.9.0,<4"]
|
||||
sms = ["aiohttp>=3.9.0,<4"]
|
||||
honcho = ["honcho-ai==2.0.1"]
|
||||
mcp = ["mcp==1.26.0"]
|
||||
homeassistant = ["aiohttp==3.13.3"]
|
||||
sms = ["aiohttp==3.13.3"]
|
||||
# Computer use — macOS background desktop control via cua-driver (MCP stdio).
|
||||
# The cua-driver binary itself is installed via `hermes tools` post-setup
|
||||
# (curl install script); this extra just pins the MCP client used to talk
|
||||
# to it, which is already provided by the `mcp` extra.
|
||||
computer-use = ["mcp>=1.2.0,<2"]
|
||||
acp = ["agent-client-protocol>=0.9.0,<1.0"]
|
||||
mistral = ["mistralai>=2.3.0,<3"]
|
||||
bedrock = ["boto3>=1.35.0,<2"]
|
||||
computer-use = ["mcp==1.26.0"]
|
||||
acp = ["agent-client-protocol==0.9.0"]
|
||||
# mistral: extra REMOVED 2026-05-12 — `mistralai` PyPI project quarantined
|
||||
# after malicious 2.4.6 release (Mini Shai-Hulud worm). Every version of
|
||||
# `mistralai` returns 404 on PyPI right now, so any pin we'd write is
|
||||
# unresolvable, which breaks `uv lock --check` in CI.
|
||||
#
|
||||
# To restore once PyPI un-quarantines:
|
||||
# 1. Verify the new release is clean (read the changelog, check Socket
|
||||
# advisory page, confirm no malicious code review findings).
|
||||
# 2. Add back: mistral = ["mistralai==<verified-version>"]
|
||||
# 3. Re-enable Mistral in:
|
||||
# - tools/lazy_deps.py (LAZY_DEPS["tts.mistral"], LAZY_DEPS["stt.mistral"])
|
||||
# - hermes_cli/tools_config.py (un-hide from provider picker)
|
||||
# - hermes_cli/web_server.py (re-add to dashboard STT options)
|
||||
# - tools/transcription_tools.py / tools/tts_tool.py (drop disabled stubs)
|
||||
# 4. Run `uv lock` to regenerate transitives.
|
||||
# 5. Optionally re-add to [all] only after a few days of clean operation.
|
||||
bedrock = ["boto3==1.42.89"]
|
||||
termux = [
|
||||
# Baseline Android / Termux path for reliable fresh installs.
|
||||
"python-telegram-bot[webhooks]>=22.6,<23",
|
||||
"python-telegram-bot[webhooks]==22.6",
|
||||
"hermes-agent[cron]",
|
||||
"hermes-agent[cli]",
|
||||
"hermes-agent[pty]",
|
||||
|
|
@ -120,35 +160,41 @@ termux-all = [
|
|||
"hermes-agent[sms]",
|
||||
"hermes-agent[web]",
|
||||
]
|
||||
dingtalk = ["dingtalk-stream>=0.20,<1", "alibabacloud-dingtalk>=2.0.0", "qrcode>=7.0,<8"]
|
||||
feishu = ["lark-oapi>=1.5.3,<2", "qrcode>=7.0,<8"]
|
||||
dingtalk = ["dingtalk-stream==0.24.3", "alibabacloud-dingtalk==2.2.42", "qrcode==7.4.2"]
|
||||
feishu = ["lark-oapi==1.5.3", "qrcode==7.4.2"]
|
||||
google = [
|
||||
# Required by the google-workspace skill (Gmail, Calendar, Drive, Contacts,
|
||||
# Sheets, Docs). Declared here so packagers (Nix, Homebrew) ship them with
|
||||
# the [all] extra and users don't hit runtime `pip install` paths that fail
|
||||
# in environments without pip (e.g. Nix-managed Python).
|
||||
"google-api-python-client>=2.100,<3",
|
||||
"google-auth-oauthlib>=1.0,<2",
|
||||
"google-auth-httplib2>=0.2,<1",
|
||||
"google-api-python-client==2.194.0",
|
||||
"google-auth-oauthlib==1.3.1",
|
||||
"google-auth-httplib2==0.3.1",
|
||||
]
|
||||
youtube = [
|
||||
# Required by skills/media/youtube-content and
|
||||
# optional-skills/productivity/memento-flashcards (youtube_quiz.py).
|
||||
# Without this declaration uv sync omits the package and both skills fail
|
||||
# at first invocation with ModuleNotFoundError (issue #22243).
|
||||
"youtube-transcript-api>=1.2.0",
|
||||
"youtube-transcript-api==1.2.4",
|
||||
]
|
||||
# `hermes dashboard` (localhost SPA + API). Not in core to keep the default install lean.
|
||||
web = ["fastapi>=0.104.0,<1", "uvicorn[standard]>=0.24.0,<1"]
|
||||
web = ["fastapi==0.133.1", "uvicorn[standard]==0.41.0"]
|
||||
rl = [
|
||||
"atroposlib @ git+https://github.com/NousResearch/atropos.git@c20c85256e5a45ad31edf8b7276e9c5ee1995a30",
|
||||
"tinker @ git+https://github.com/thinking-machines-lab/tinker.git@30517b667f18a3dfb7ef33fb56cf686d5820ba2b",
|
||||
"fastapi>=0.104.0,<1",
|
||||
"uvicorn[standard]>=0.24.0,<1",
|
||||
"wandb>=0.15.0,<1",
|
||||
"fastapi==0.133.1",
|
||||
"uvicorn[standard]==0.41.0",
|
||||
"wandb==0.25.1",
|
||||
]
|
||||
yc-bench = ["yc-bench @ git+https://github.com/collinear-ai/yc-bench.git@bfb0c88062450f46341bd9a5298903fc2e952a5c ; python_version >= '3.12'"]
|
||||
all = [
|
||||
"hermes-agent[anthropic]",
|
||||
"hermes-agent[exa]",
|
||||
"hermes-agent[firecrawl]",
|
||||
"hermes-agent[parallel-web]",
|
||||
"hermes-agent[fal]",
|
||||
"hermes-agent[edge-tts]",
|
||||
"hermes-agent[modal]",
|
||||
"hermes-agent[daytona]",
|
||||
"hermes-agent[vercel]",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue