fixup: nix builds

This commit is contained in:
ethernet
2026-09-12 20:02:44 -04:00
parent 76e663ee45
commit a0d425d60f
5 changed files with 128 additions and 34 deletions

View File

@@ -6,7 +6,8 @@
// one place.
import { mkdirSync } from 'node:fs'
import { createRequire } from 'node:module'
// without this rename it breaks due to some bundler injection of the same name
import { createRequire as nodeCreateRequire } from 'node:module'
import path from 'node:path'
import type devIdentity from '../product-identity.cjs'
@@ -19,7 +20,7 @@ declare const __HERMES_PRODUCT_IDENTITY__: ProductIdentity
/** The baked identity of this artifact (dev bundles derive it live). */
export const PRODUCT_IDENTITY: Readonly<ProductIdentity> =
typeof __HERMES_PRODUCT_IDENTITY__ === 'undefined'
? Object.freeze(createRequire(import.meta.url)('../product-identity.cjs') as ProductIdentity)
? Object.freeze(nodeCreateRequire(import.meta.url)('../product-identity.cjs') as ProductIdentity)
: Object.freeze(__HERMES_PRODUCT_IDENTITY__)
/** Pin before the first userData lookup and single-instance lock. Electron's

View File

@@ -71,6 +71,7 @@ let
"apps/desktop"
"apps/shared"
"scripts/build/desktop.mjs"
"scripts/build/freshness.mjs"
"scripts/build/frontend-common.mjs"
];
pname = "hermes-desktop-renderer";

View File

@@ -5,6 +5,7 @@ hermesNpmLib.buildNpmPackage {
"ui-tui"
"apps/shared"
"scripts/build/tui.mjs"
"scripts/build/freshness.mjs"
"scripts/build/frontend-common.mjs"
];

View File

@@ -5,6 +5,7 @@ hermesNpmLib.buildNpmPackage {
"web"
"apps/shared"
"scripts/build/web.mjs"
"scripts/build/freshness.mjs"
"scripts/build/frontend-common.mjs"
];

View File

@@ -48,9 +48,9 @@ dependencies = [
"httpx[socks]==0.28.1",
"rich==14.3.3",
"tenacity==9.1.4",
"tomli-w==1.2.0", # Preserve relative dependency paths in staged plugin metadata.
"tomli-w==1.2.0", # Preserve relative dependency paths in staged plugin metadata.
"ruamel.yaml==0.18.17",
"requests==2.33.0", # CVE-2026-25645
"requests==2.33.0", # CVE-2026-25645
"jinja2==3.1.6",
# Document-to-Markdown extraction for read_file (PDF, legacy Office,
# OpenDocument, RTF, EPUB) + typed NeedsOcrError for scanned pages.
@@ -88,7 +88,7 @@ dependencies = [
# it out of the lazy-install path that exists only for the heavy matrix deps.
"Markdown==3.10.2",
# Skills Hub (GitHub App JWT auth — optional, only needed for bot identity)
"PyJWT[crypto]==2.13.0", # PYSEC-2026-175/177/178/179
"PyJWT[crypto]==2.13.0", # PYSEC-2026-175/177/178/179
# urllib3 2.7.0 fixes GHSA-mf9v-mfxr-j63j (decompression-bomb bypass)
# and GHSA-qccp-gfcp-xxvc (header leak across origins).
"urllib3>=2.7.0,<3",
@@ -103,7 +103,7 @@ dependencies = [
# A pin here is not sufficient on its own. alibabacloud-tea-openapi caps
# cryptography<49, so [tool.uv] also holds an override. Read that comment
# before you move this version.
"cryptography==50.0.1", # CVE-2026-69247, GHSA-m2h6-j472-rp4c, GHSA-jwv3-5hgf-82ww, CVE-2026-39892, CVE-2026-34073, GHSA-537c-gmf6-5ccf
"cryptography==50.0.1", # CVE-2026-69247, GHSA-m2h6-j472-rp4c, GHSA-jwv3-5hgf-82ww, CVE-2026-39892, CVE-2026-34073, GHSA-537c-gmf6-5ccf
# 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
@@ -190,7 +190,7 @@ dependencies = [
[project.optional-dependencies]
# Native Anthropic provider — only needed when provider=anthropic (not via
# OpenRouter or other aggregators).
anthropic = ["anthropic==0.87.0"] # CVE-2026-34450, CVE-2026-34452
anthropic = ["anthropic==0.87.0"] # CVE-2026-34450, CVE-2026-34452
# 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"]
@@ -212,16 +212,36 @@ kittentts = [
"soundfile==0.14.0; python_version < '3.13'",
]
# onnxruntime has no Intel macOS wheel. Piper has no Windows ARM64 wheel.
piper = ["piper-tts==1.8.0; (platform_machine != 'ARM64' or sys_platform != 'win32') and (platform_machine != 'x86_64' or sys_platform != 'darwin')"]
piper = [
"piper-tts==1.8.0; (platform_machine != 'ARM64' or sys_platform != 'win32') and (platform_machine != 'x86_64' or sys_platform != 'darwin')",
]
modal = ["modal==1.3.4"]
daytona = ["daytona==0.155.0"]
vercel = ["vercel==0.7.2"]
hindsight = ["hindsight-client==0.6.1"]
google-meet = ["playwright==1.62.0", "websockets==15.0.1"]
dev = ["debugpy==1.8.20", "pytest==9.1.1", "pytest-asyncio==1.3.0", "mcp==2.0.0", "httpx2==2.7.0", "starlette==1.3.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==83.0.0"] # starlette: CVE-2026-48710; setuptools: 83 (torch >=2.13 requires setuptools 83)
dev = [
"debugpy==1.8.20",
"pytest==9.1.1",
"pytest-asyncio==1.3.0",
"mcp==2.0.0",
"httpx2==2.7.0",
"starlette==1.3.1",
"ty==0.0.21",
"ruff==0.15.10",
"setuptools==83.0.0",
] # starlette: CVE-2026-48710; setuptools: 83 (torch >=2.13 requires setuptools 83)
messaging = ["python-telegram-bot[webhooks]==22.8", "discord.py[voice]==2.7.1", "aiohttp==3.14.3", "brotlicffi==1.2.0.2", "slack-bolt==1.30.0", "slack-sdk==3.44.1", "qrcode==7.4.2"] # aiohttp 3.14.3: prior CVEs + GHSA-cq5v-8q36-5273/GHSA-mfx4-hv73-q22v/GHSA-mq44-7p77-q5h7
cron = [] # croniter is now a core dependency; this extra kept for back-compat
messaging = [
"python-telegram-bot[webhooks]==22.8",
"discord.py[voice]==2.7.1",
"aiohttp==3.14.3",
"brotlicffi==1.2.0.2",
"slack-bolt==1.30.0",
"slack-sdk==3.44.1",
"qrcode==7.4.2",
] # aiohttp 3.14.3: prior CVEs + GHSA-cq5v-8q36-5273/GHSA-mfx4-hv73-q22v/GHSA-mq44-7p77-q5h7
cron = [] # croniter is now a core dependency; this extra kept for back-compat
slack = ["slack-bolt==1.30.0", "slack-sdk==3.44.1", "aiohttp==3.14.3"]
# Matrix gateway deps. `mautrix[encryption]` pulls `python-olm`, whose
# vendored libolm (archived 2023) cannot build on Windows or modern macOS:
@@ -236,7 +256,7 @@ matrix = [
"asyncpg==0.31.0; sys_platform == 'linux'",
"aiohttp-socks==0.11.0; sys_platform == 'linux'",
"aiohttp==3.14.3; sys_platform == 'linux'",
] # aiohttp 3.14.3: prior CVEs + GHSA-cq5v-8q36-5273/GHSA-mfx4-hv73-q22v/GHSA-mq44-7p77-q5h7 (mautrix/aiohttp-socks only cap aiohttp<4 / >=3.10, so pin the patched floor directly)
] # aiohttp 3.14.3: prior CVEs + GHSA-cq5v-8q36-5273/GHSA-mfx4-hv73-q22v/GHSA-mq44-7p77-q5h7 (mautrix/aiohttp-socks only cap aiohttp<4 / >=3.10, so pin the patched floor directly)
# WeCom callback-mode adapter — parses untrusted XML POST bodies from
# WeCom-controlled callback endpoints, so we use defusedxml (drop-in
@@ -279,14 +299,22 @@ honcho = ["honcho-ai==2.2.0"]
telegram = ["python-telegram-bot[webhooks]==22.8"]
discord = ["discord.py[voice]==2.7.1"]
# stt-whisper + audio-io compose [voice] (faster-whisper / sounddevice / numpy).
stt-whisper = ["faster-whisper==1.2.1; (platform_machine != 'ARM64' or sys_platform != 'win32') and (platform_machine != 'x86_64' or platform_system != 'Darwin')"]
stt-whisper = [
"faster-whisper==1.2.1; (platform_machine != 'ARM64' or sys_platform != 'win32') and (platform_machine != 'x86_64' or platform_system != 'Darwin')",
]
audio-io = ["sounddevice==0.5.5", "numpy==2.4.3"]
# WeChat voice decode (tools/transcription_tools.py self-heal path).
silk = ["pilk==0.2.4"]
# Wake-engine sub-extras — lazy per-engine installs so a user who only wants
# sherpa-onnx doesn't pull the whole [wake] bundle. Pins match [wake].
wake-openwakeword = ["pyopen-wakeword==1.1.0; (platform_machine != 'ARM64' or sys_platform != 'win32') and (platform_machine != 'x86_64' or sys_platform != 'darwin')"]
wake-sherpa = ["sherpa-onnx==1.13.8", "sentencepiece==0.2.2", "pypinyin==0.55.0"]
wake-openwakeword = [
"pyopen-wakeword==1.1.0; (platform_machine != 'ARM64' or sys_platform != 'win32') and (platform_machine != 'x86_64' or sys_platform != 'darwin')",
]
wake-sherpa = [
"sherpa-onnx==1.13.8",
"sentencepiece==0.2.2",
"pypinyin==0.55.0",
]
wake-porcupine = ["pvporcupine==4.0.3"]
# Google Chat service account push (gateway/platforms/google_chat) — pubsub
# subscriber for the Chat API's Cloud Pub/Sub delivery.
@@ -294,7 +322,9 @@ wake-porcupine = ["pvporcupine==4.0.3"]
# would build its sdist and fail — gate the whole extra off there like
# faster-whisper. The adapter's pubsub import is lazy, so this just makes the
# google-chat platform unavailable on win32-arm64.
google-chat = ["google-cloud-pubsub==2.39.2; platform_machine != 'ARM64' or sys_platform != 'win32'"]
google-chat = [
"google-cloud-pubsub==2.39.2; platform_machine != 'ARM64' or sys_platform != 'win32'",
]
# Document extraction (read_file anydoc converter self-heal) — core already
# bundles firecrawl-anydoc==0.2.4; the extra exists so a lean/broken install
# can re-sync exactly the pin from core.
@@ -306,7 +336,9 @@ supermemory = ["supermemory==3.50.0"]
# mem0ai pulls qdrant-client → grpcio, which has no win_arm64 wheel — same
# win32-arm64 gate as google-chat. mem0 is lazy-installed, so on arm64 the
# provider is simply unavailable.
mem0 = ["mem0ai==2.0.10; platform_machine != 'ARM64' or sys_platform != 'win32'"]
mem0 = [
"mem0ai==2.0.10; platform_machine != 'ARM64' or sys_platform != 'win32'",
]
# Image resize recovery for the vision tools. Pillow is now a CORE dependency
# (see the main `dependencies` list above) since the byte/pixel shrink paths are on
# the default vision-embed path and the mid-session lazy install deadlocked the
@@ -333,16 +365,27 @@ pty = []
# resolution. Hermes' own `httpx[socks]==0.28.1` in [dependencies] is
# unaffected — the two distributions install side by side under different
# module names.
mcp = ["mcp==2.0.0", "httpx2==2.7.0", "starlette==1.3.1"] # starlette: CVE-2026-48710
mcp = [
"mcp==2.0.0",
"httpx2==2.7.0",
"starlette==1.3.1",
] # starlette: CVE-2026-48710
# Backwards-compatible no-op alias. Relay is a core dependency on supported
# wheel targets and intentionally unavailable on other platforms.
nemo-relay = []
homeassistant = ["aiohttp==3.14.3"]
sms = ["aiohttp==3.14.3"]
teams = ["microsoft-teams-apps==2.0.13.4", "aiohttp==3.14.3"] # aiohttp 3.14.3: prior CVEs + GHSA-cq5v-8q36-5273/GHSA-mfx4-hv73-q22v/GHSA-mq44-7p77-q5h7
teams = [
"microsoft-teams-apps==2.0.13.4",
"aiohttp==3.14.3",
] # aiohttp 3.14.3: prior CVEs + GHSA-cq5v-8q36-5273/GHSA-mfx4-hv73-q22v/GHSA-mq44-7p77-q5h7
# Computer use talks to PM's pinned cua-driver over MCP stdio. This extra
# provides the MCP client, independently of the native driver package.
computer-use = ["mcp==2.0.0", "httpx2==2.7.0", "starlette==1.3.1"] # starlette: CVE-2026-48710
computer-use = [
"mcp==2.0.0",
"httpx2==2.7.0",
"starlette==1.3.1",
] # starlette: CVE-2026-48710
acp = ["agent-client-protocol==0.9.0"]
# mistral: Voxtral STT + TTS. Pinned to an exact verified-clean version.
# The `mistralai` PyPI project was quarantined 2026-05-12 after the malicious
@@ -357,7 +400,10 @@ mistral = ["mistralai==2.4.8"]
# OTLP/HTTP exporter for monitoring.gateway_health_export. Lazy-installed via
# PM on first use; never a core dependency and deliberately
# NOT in [all].
otlp = ["opentelemetry-sdk==1.39.1", "opentelemetry-exporter-otlp-proto-http==1.39.1"]
otlp = [
"opentelemetry-sdk==1.39.1",
"opentelemetry-exporter-otlp-proto-http==1.39.1",
]
langfuse = ["langfuse==4.15.2"]
bedrock = ["boto3==1.42.89"]
vertex = ["google-auth==2.55.1"]
@@ -382,7 +428,11 @@ termux-all = [
"hermes-agent[web]",
"hermes-agent[pty]",
]
dingtalk = ["dingtalk-stream==0.24.3", "alibabacloud-dingtalk==2.2.42", "qrcode==7.4.2"]
dingtalk = [
"dingtalk-stream==0.24.3",
"alibabacloud-dingtalk==2.2.42",
"qrcode==7.4.2",
]
feishu = ["lark-oapi==1.6.8", "qrcode==7.4.2"]
google = [
# Required by the google-workspace skill (Gmail, Calendar, Drive, Contacts,
@@ -409,7 +459,12 @@ youtube = [
# `hermes dashboard` (localhost SPA + API). Not in core to keep the default install lean.
# starlette==1.3.1 pinned for CVE-2026-48710 (BadHost) — fastapi pulls Starlette
# transitively and pre-1.0.1 is the vulnerable range. See the mcp extra above.
web = ["fastapi==0.133.1", "uvicorn[standard]==0.41.0", "starlette==1.3.1", "python-multipart==0.0.32"]
web = [
"fastapi==0.133.1",
"uvicorn[standard]==0.41.0",
"starlette==1.3.1",
"python-multipart==0.0.32",
]
all = [
# Policy (2026-05-12): `[all]` includes only extras that genuinely
# CAN'T be prepared on demand by PM — i.e. things every
@@ -665,10 +720,38 @@ youtube-transcript-api = false
# and the drift broke installed wheels. Do not add the list back.
[tool.setuptools.packages.find]
include = ["agent", "agent.*", "tools", "tools.*", "hermes_cli", "hermes_cli.*", "hermes_wisdom", "hermes_wisdom.*", "gateway", "gateway.*", "tui_gateway", "tui_gateway.*", "cron", "cron.*", "acp_adapter", "plugins", "plugins.*", "providers", "providers.*", "pm", "pm.*"]
include = [
"agent",
"agent.*",
"tools",
"tools.*",
"hermes_cli",
"hermes_cli.*",
"hermes_wisdom",
"hermes_wisdom.*",
"gateway",
"gateway.*",
"tui_gateway",
"tui_gateway.*",
"cron",
"cron.*",
"acp_adapter",
"plugins",
"plugins.*",
"providers",
"providers.*",
"pm",
"pm.*",
"tools",
"tools.*",
]
[tool.setuptools.package-data]
hermes_cli = ["observability/schemas/*.json", "data/*.json", "local_runtime/*.json"]
hermes_cli = [
"observability/schemas/*.json",
"data/*.json",
"local_runtime/*.json",
]
# gateway/assets/ ships status_phrases.yaml and the Telegram BotFather
# screenshot. Without this, sealed venvs (uv2nix) silently lose both —
# status phrases fall back to the tiny hardcoded set and the Telegram
@@ -687,13 +770,13 @@ pm = ["lock.json", "artifact-mirror.json", "pyproject.toml", "uv.lock"]
[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"integration: marks tests requiring external services (API keys, Modal, etc.)",
"real_concurrent_gate: opt out of the autouse stub that disables _detect_concurrent_hermes_instances",
"real_agent_prewarm: opt out of the autouse stub that disables the tui_gateway deferred agent pre-warm timer",
"requires_wal: needs the runtime to actually enable SQLite WAL mode (skipped where Hermes falls back to journal_mode=DELETE)",
"no_isolate: opt out of per-file subprocess isolation (tests share mutable module-level state)",
"ssh: marks tests requiring a reachable SSH server (skipped in normal CI)",
"platforms(*specs, arch=None, arch_negate=False): run only on hosts matching at least one spec — linux/macos/windows/posix/any, 'not X' negation, optional arch filter",
"integration: marks tests requiring external services (API keys, Modal, etc.)",
"real_concurrent_gate: opt out of the autouse stub that disables _detect_concurrent_hermes_instances",
"real_agent_prewarm: opt out of the autouse stub that disables the tui_gateway deferred agent pre-warm timer",
"requires_wal: needs the runtime to actually enable SQLite WAL mode (skipped where Hermes falls back to journal_mode=DELETE)",
"no_isolate: opt out of per-file subprocess isolation (tests share mutable module-level state)",
"ssh: marks tests requiring a reachable SSH server (skipped in normal CI)",
"platforms(*specs, arch=None, arch_negate=False): run only on hosts matching at least one spec — linux/macos/windows/posix/any, 'not X' negation, optional arch filter",
]
# integration tests take way too long to run in the normal CI environments
addopts = "-m 'not integration'"
@@ -706,7 +789,7 @@ unknown-argument = "warn"
redundant-cast = "ignore"
[tool.ruff]
preview = true # required for PLW1514 (unspecified-encoding) — preview rule
preview = true # required for PLW1514 (unspecified-encoding) — preview rule
[tool.ruff.lint]
# All other lints are intentionally disabled (see comment history on this
@@ -744,7 +827,14 @@ select = ["PLW1514", "ASYNC210", "ASYNC220", "ASYNC221", "ASYNC251", "TID251"]
[tool.ruff.lint.per-file-ignores]
"pm/**" = ["TID251"]
# Tests can intentionally exercise locale-encoding edge cases.
"tests/**" = ["PLW1514", "ASYNC210", "ASYNC220", "ASYNC221", "ASYNC251", "TID251"]
"tests/**" = [
"PLW1514",
"ASYNC210",
"ASYNC220",
"ASYNC221",
"ASYNC251",
"TID251",
]
# Skills and plugins are partially user-authored — their own conventions.
"skills/**" = ["PLW1514"]
"optional-skills/**" = ["PLW1514"]