fix(docker): ship tmux and the Office/PDF libraries; document skills keep a persistent side env

Hosted Docker instances could not run the hermes-agent skill's tmux spawning
recipe (no tmux) or create Office/PDF files: the docx/xlsx/powerpoint/pdf
skills said `pip install ...`, but the image's sealed venv has no pip and the
root fs resets on restart.

- Dockerfile: tmux in the runtime apt layer (+3 MB); bake a new [documents]
  extra (python-docx, openpyxl, python-pptx, reportlab, pypdf) into the
  sealed venv (+14 MB compressed, ~+38 MB on disk).
- pyproject/uv.lock: the [documents] extra, exact pins, outside [all];
  pm/extras ANCHORS gets its import anchors.
- docx/xlsx/powerpoint/pdf (+ optional excel-author/pptx-author): use
  `python` when it already imports the libs, otherwise a persistent side
  venv at $HERMES_HOME/environments/documents (recreated when its Python is
  gone); pdf script hints and the OCR reference point there, never at pip
  into Hermes's interpreter.
- hermes-agent skill: send text and Enter as separate tmux keystrokes (one
  burst lands as a newline, verified live), plus a no-tmux fallback via
  terminal(background=true, pty=true) + process_manage write.

Found on Hermes Cloud staging.
This commit is contained in:
Teknium committed 2026-10-28 19:26:17 -07:00
1 parent 93286ffd87
commit b357ebbfc4
24 files changed
+259 -37

No files matched your search

+8 -2
View File
@@ -74,9 +74,11 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/tools
# must be declared here. The list is the `ldd ... | grep "not found"` set of
# the pinned chrome binary in this base image, mapped to trixie package
# names (see .hermes/plans/termux-removal-commit-spec.md).
# tmux backs the hermes-agent skill's interactive spawning recipe; the runtime
# user cannot apt-get it later (no root, and the root fs resets on restart).
RUN apt-get -o Acquire::Retries=3 update && \
apt-get -o Acquire::Retries=3 install -y --no-install-recommends \
ca-certificates curl iputils-ping python3 python-is-python3 gcc g++ make cmake python3-dev python3-venv libffi-dev libolm-dev libatomic1 procps git openssh-client docker-cli xz-utils \
ca-certificates curl iputils-ping python3 python-is-python3 gcc g++ make cmake python3-dev python3-venv libffi-dev libolm-dev libatomic1 procps git openssh-client docker-cli xz-utils tmux \
libasound2t64 libatk-bridge2.0-0t64 libatk1.0-0t64 libatspi2.0-0t64 libcairo2 libcups2t64 libdbus-1-3 libgbm1 libglib2.0-0t64 libnspr4 libnss3 libpango-1.0-0 libx11-6 libxcb1 libxcomposite1 libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxrandr2 && \
rm -rf /var/lib/apt/lists/*
@@ -284,6 +286,10 @@ FROM runtime_base AS python_deps
# baked here (aiohttp, so no API server). stage2's dependency refresh
# restores this baseline into volumes that already lost it.
#
# The [documents] extra (python-docx, openpyxl, python-pptx, reportlab, pypdf;
# ~25 MB) is baked so the docx/xlsx/powerpoint/pdf skills work out of the box:
# this venv is sealed and has no pip, so the agent could not add them itself.
#
# Source binding is created after the source copy below.
COPY pyproject.toml uv.lock ./
RUN touch ./README.md
@@ -296,7 +302,7 @@ RUN python3 -m pm.build_env --source /opt/hermes --python /usr/local/bin/python3
--cache /opt/hermes/uv-cache \
--out /opt/hermes/.venv --no-install-project --sealed --record-selection \
--extra all --extra messaging --extra otlp --extra anthropic --extra bedrock \
--extra azure-identity --extra matrix --extra google-chat --extra fal
--extra azure-identity --extra matrix --extra google-chat --extra fal --extra documents
# Icons render on the runtime environment: Pillow and resvg-py are core
# dependencies. A stage of its own so the frontend stage keeps building its
+13 -1
View File
@@ -25,10 +25,22 @@ Adapted from Anthropic's `xlsx-author` and `audit-xls` skills in the [anthropics
## Setup
Use `python` when it already imports `openpyxl` (the Hermes Docker image
ships it). Otherwise install into a persistent side environment under the
Hermes home, never into the interpreter that runs Hermes:
```bash
pip install "openpyxl>=3.0"
export PY=python
if ! "$PY" -c "import openpyxl" 2>/dev/null; then
DOCS_ENV="${HERMES_HOME:-$HOME/.hermes}/environments/documents"
PY="$DOCS_ENV/bin/python"
"$PY" -c "" 2>/dev/null || python -m venv --clear "$DOCS_ENV"
"$PY" -c "import openpyxl" 2>/dev/null || "$PY" -m pip install -q "openpyxl>=3.0"
fi
```
Run the model-building Python with `"$PY"`.
## Core conventions (non-negotiable)
### Blue / black / green cell color
+13 -1
View File
@@ -26,10 +26,22 @@ For the broader, already-shipped PowerPoint authoring skill (slides, speaker not
## Setup
Use `python` when it already imports `pptx` (the Hermes Docker image ships
it). Otherwise install into a persistent side environment under the Hermes
home, never into the interpreter that runs Hermes:
```bash
pip install "python-pptx>=0.6"
export PY=python
if ! "$PY" -c "import pptx" 2>/dev/null; then
DOCS_ENV="${HERMES_HOME:-$HOME/.hermes}/environments/documents"
PY="$DOCS_ENV/bin/python"
"$PY" -c "" 2>/dev/null || python -m venv --clear "$DOCS_ENV"
"$PY" -c "import pptx" 2>/dev/null || "$PY" -m pip install -q "python-pptx>=0.6"
fi
```
Run the deck-building Python with `"$PY"`.
## Core conventions
### One idea per slide
+1
View File
@@ -59,6 +59,7 @@ ANCHORS: dict[str, str | tuple[str, ...]] = {
"acp": "acp",
"web": "fastapi",
"doc-extract": "anydoc",
"documents": ("docx", "openpyxl", "pptx", "reportlab", "pypdf"),
"computer-use": "mcp",
"trace-upload": "huggingface_hub",
# Pillow resize recovery for vision tools (the `vision` extra is a no-op
+11
View File
@@ -373,6 +373,17 @@ google-chat = [
# bundles firecrawl-anydoc==0.2.4; the extra exists so a lean/broken install
# can re-sync exactly the pin from core.
doc-extract = ["firecrawl-anydoc==0.2.4"]
# Office/PDF authoring libraries for skills/productivity/{docx,xlsx,powerpoint,pdf}.
# Baked into the Docker image (Dockerfile `--extra documents`), whose sealed
# venv has no pip and whose root fs resets on restart; ~25 MB there. Not in
# [all]: plain installs keep the skills' persistent side environment instead.
documents = [
"python-docx==1.2.0",
"openpyxl==3.1.5",
"python-pptx==1.0.2",
"reportlab==5.1.0",
"pypdf==6.21.2",
]
trace-upload = ["huggingface-hub==1.33.0"]
# Cloud memory providers are opt-in PM extras. They stay outside [all]
# so a quarantined release cannot break fresh installs.
@@ -141,23 +141,34 @@ terminal(command="hermes chat -q 'Set up CI/CD for ~/myapp'", background=true)
### Interactive PTY Mode (via tmux)
Hermes uses prompt_toolkit, which requires a real terminal. Use tmux for interactive spawning:
Hermes uses prompt_toolkit, which requires a real terminal. Use tmux for interactive spawning. Send the text and `Enter` as two `send-keys` calls: text and Enter in one call reach Hermes as one burst, and the Enter lands as a newline inside the message instead of submitting it.
```
# Start
terminal(command="tmux new-session -d -s agent1 -x 120 -y 40 'hermes'", timeout=10)
# Wait for startup, then send a message
terminal(command="sleep 8 && tmux send-keys -t agent1 'Build a FastAPI auth service' Enter", timeout=15)
terminal(command="sleep 8 && tmux send-keys -t agent1 'Build a FastAPI auth service' && sleep 0.5 && tmux send-keys -t agent1 Enter", timeout=15)
# Read output
terminal(command="sleep 20 && tmux capture-pane -t agent1 -p", timeout=5)
# Send follow-up
terminal(command="tmux send-keys -t agent1 'Add rate limiting middleware' Enter", timeout=5)
terminal(command="tmux send-keys -t agent1 'Add rate limiting middleware' && sleep 0.5 && tmux send-keys -t agent1 Enter", timeout=5)
# Exit
terminal(command="tmux send-keys -t agent1 '/exit' Enter && sleep 2 && tmux kill-session -t agent1", timeout=10)
terminal(command="tmux send-keys -t agent1 '/exit' && sleep 0.5 && tmux send-keys -t agent1 Enter && sleep 2 && tmux kill-session -t agent1", timeout=10)
```
**No tmux** (`command -v tmux` prints nothing, e.g. an older Hermes Docker image): run Hermes as a PTY background process and drive it with `process_manage`. Stdin only exists with `pty=true`. Send the text and the Enter key (`"\r"`) as two separate `write` calls, for the same reason as with tmux; `submit` appends `"\n"`, which Hermes reads as a newline, never as Enter.
```
terminal(command="hermes", background=true, pty=true) # returns session_id
process_manage(action="write", session_id="<id>", data="Build a FastAPI auth service")
process_manage(action="write", session_id="<id>", data="\r")
process_manage(action="log", session_id="<id>", limit=40) # read the screen
process_manage(action="write", session_id="<id>", data="/exit")
process_manage(action="write", session_id="<id>", data="\r")
```
### Multi-Agent Coordination
@@ -165,15 +176,15 @@ terminal(command="tmux send-keys -t agent1 '/exit' Enter && sleep 2 && tmux kill
```
# Agent A: backend
terminal(command="tmux new-session -d -s backend -x 120 -y 40 'hermes -w'", timeout=10)
terminal(command="sleep 8 && tmux send-keys -t backend 'Build REST API for user management' Enter", timeout=15)
terminal(command="sleep 8 && tmux send-keys -t backend 'Build REST API for user management' && sleep 0.5 && tmux send-keys -t backend Enter", timeout=15)
# Agent B: frontend
terminal(command="tmux new-session -d -s frontend -x 120 -y 40 'hermes -w'", timeout=10)
terminal(command="sleep 8 && tmux send-keys -t frontend 'Build React dashboard for user management' Enter", timeout=15)
terminal(command="sleep 8 && tmux send-keys -t frontend 'Build React dashboard for user management' && sleep 0.5 && tmux send-keys -t frontend Enter", timeout=15)
# Check progress, relay context between them
terminal(command="tmux capture-pane -t backend -p | tail -30", timeout=5)
terminal(command="tmux send-keys -t frontend 'Here is the API schema from the backend agent: ...' Enter", timeout=5)
terminal(command="tmux send-keys -t frontend 'Here is the API schema from the backend agent: ...' && sleep 0.5 && tmux send-keys -t frontend Enter", timeout=5)
```
### Session Resume
@@ -192,7 +203,7 @@ terminal(command="tmux new-session -d -s resumed 'hermes --resume 20260225_14305
- **Use `-w` (worktree mode)** when spawning agents that edit code — prevents git conflicts
- **Set timeouts** for one-shot mode — complex tasks can take 5-10 minutes
- **Use `hermes chat -q` for fire-and-forget** — no PTY needed
- **Use tmux for interactive sessions** — raw PTY mode has `\r` vs `\n` issues with prompt_toolkit
- **Prefer tmux for interactive sessions when it is installed** — `capture-pane` reads the rendered screen; the PTY-background fallback only has the raw output log
- **For scheduled tasks**, use the `cronjob` tool instead of spawning — handles delivery and retry
- **"delegate_task is capped at N" reports** — see `references/delegate-task-concurrency-diagnosis.md`. Three real cap paths in Hermes; if none fired, the model is self-limiting and rationalising it as "the runtime caps."
- **"Can $external_app use my Nous Portal subscription / OAuth?"** — see `references/portal-auth-for-third-party-apps.md`. Walk the user through three layers (plugin-vs-app, what Portal actually exposes, local-broker-proxy option).
+20 -2
View File
@@ -36,8 +36,26 @@ fields, and package health checks. It does not render documents itself
## Prerequisites
- Python 3.10+ with `python-docx` installed:
`pip install python-docx` (import name is `docx`; lxml comes with it).
- Python 3.10+ with `python-docx` (import name `docx`; lxml comes with it).
- Pick the interpreter with `terminal`. Use `python` when it already imports
the libraries (the Hermes Docker image ships them); otherwise use a
persistent side environment under the Hermes home, which survives container
restarts and never touches the Hermes install itself:
```bash
export PY=python
if ! "$PY" -c "import docx" 2>/dev/null; then
DOCS_ENV="${HERMES_HOME:-$HOME/.hermes}/environments/documents"
PY="$DOCS_ENV/bin/python"
"$PY" -c "" 2>/dev/null || python -m venv --clear "$DOCS_ENV" # new, or broken by a Python upgrade
"$PY" -c "import docx" 2>/dev/null || "$PY" -m pip install -q python-docx
fi
"$PY" scripts/docx_create.py --help
```
Run every helper below with `"$PY"`. On Windows the side interpreter is
`%HERMES_HOME%\environments\documents\Scripts\python.exe`. Never
`pip install` into the interpreter that runs Hermes.
- Comments `add` uses the native API on python-docx >= 1.2 and an XML
fallback on older versions — both are automatic.
- For image blocks: the image files must exist locally (PNG/JPEG).
+21 -3
View File
@@ -31,9 +31,27 @@ Create PDFs from structured specs, build and fill AcroForm forms (with layout li
## Prerequisites
- Python 3.10+ with `pypdf`, `reportlab`, `pdfplumber`:
`python -m pip install pypdf reportlab pdfplumber`
- Optional, for page rasterization (`pdf_page_image.py`, overlay rendering): `python -m pip install pypdfium2`, or poppler's `pdftoppm` on PATH. Scripts fall back pypdfium2 → pdftoppm and report `{"rendered": false, "missing": [...]}` (exit 0) when neither exists.
- Python 3.10+ with `pypdf`, `reportlab`, `pdfplumber`.
- Pick the interpreter with `terminal`. Use `python` when it already imports
the libraries (the Hermes Docker image ships them); otherwise use a
persistent side environment under the Hermes home, which survives container
restarts and never touches the Hermes install itself:
```bash
export PY=python
if ! "$PY" -c "import pypdf, reportlab, pdfplumber" 2>/dev/null; then
DOCS_ENV="${HERMES_HOME:-$HOME/.hermes}/environments/documents"
PY="$DOCS_ENV/bin/python"
"$PY" -c "" 2>/dev/null || python -m venv --clear "$DOCS_ENV" # new, or broken by a Python upgrade
"$PY" -c "import pypdf, reportlab, pdfplumber" 2>/dev/null || "$PY" -m pip install -q pypdf reportlab pdfplumber
fi
"$PY" scripts/pdf_create.py --help
```
Run every helper below with `"$PY"`. On Windows the side interpreter is
`%HERMES_HOME%\environments\documents\Scripts\python.exe`. Never
`pip install` into the interpreter that runs Hermes.
- Optional, for page rasterization (`pdf_page_image.py`, overlay rendering): `pypdfium2` installed into that side environment (`"$DOCS_ENV/bin/python" -m pip install pypdfium2`, then use it as `$PY`), or poppler's `pdftoppm` on PATH. Scripts fall back pypdfium2 → pdftoppm and report `{"rendered": false, "missing": [...]}` (exit 0) when neither exists.
- Each helper script checks imports lazily and prints an install hint if a dependency is missing.
## How to Run
@@ -51,8 +51,12 @@ If the user needs marker capabilities but the system lacks ~5GB free disk:
## pymupdf (lightweight)
Install into the persistent documents environment from the pdf skill's
Prerequisites (`$DOCS_ENV`, ~110 MB with its deps; never into Hermes's own
interpreter), then run the helper with `"$PY"`:
```bash
pip install pymupdf pymupdf4llm
"$DOCS_ENV/bin/python" -m pip install -q pymupdf pymupdf4llm && PY="$DOCS_ENV/bin/python"
```
**Via helper script**:
@@ -83,7 +87,7 @@ for page in doc:
# Check disk space first
python scripts/extract_marker.py --check
pip install marker-pdf
"$DOCS_ENV/bin/python" -m pip install marker-pdf # same side environment as pymupdf
```
**Via helper script**:
+1 -1
View File
@@ -26,7 +26,7 @@ def available_backends() -> list[str]:
def missing_hints() -> list[str]:
"""Install hints for when no backend is available."""
return [
"python3 -m pip install pypdfium2",
"pypdfium2 in the persistent documents environment (pdf skill, Prerequisites)",
"poppler-utils (provides pdftoppm), e.g. apt-get install poppler-utils",
]
@@ -47,7 +47,7 @@ def build_pdf(spec: dict, out_path: str) -> int:
TableStyle,
)
except ImportError:
print("Missing dependency: install with 'python3 -m pip install reportlab'", file=sys.stderr)
print("Missing dependency: 'reportlab' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
page_size = letter if str(spec.get("page_size", "A4")).lower() == "letter" else A4
@@ -35,7 +35,7 @@ def main() -> int:
from pypdf import PdfReader, PdfWriter
from pypdf.generic import BooleanObject, NameObject
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
print("Missing dependency: 'pypdf' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
with open(args.fields_json, encoding="utf-8") as fh:
@@ -54,7 +54,7 @@ def build_form(spec: dict, out_path: str) -> int:
from reportlab.lib import colors
from reportlab.pdfgen import canvas
except ImportError:
print("Missing dependency: install with 'python3 -m pip install reportlab'", file=sys.stderr)
print("Missing dependency: 'reportlab' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
width, height = _page_size(spec)
+1 -1
View File
@@ -24,7 +24,7 @@ def main() -> int:
try:
from pypdf import PdfReader, PdfWriter
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
print("Missing dependency: 'pypdf' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
writer = PdfWriter()
+1 -1
View File
@@ -46,7 +46,7 @@ def main() -> int:
try:
from pypdf import PdfReader, PdfWriter
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
print("Missing dependency: 'pypdf' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
reader = PdfReader(args.pdf)
@@ -62,7 +62,7 @@ def main() -> int:
try:
from pypdf import PdfReader
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
print("Missing dependency: 'pypdf' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
reader = PdfReader(args.pdf)
if reader.is_encrypted:
+1 -1
View File
@@ -21,7 +21,7 @@ def _need(module: str, package: str):
try:
return __import__(module)
except ImportError:
print(f"Missing dependency: install with 'python3 -m pip install {package}'", file=sys.stderr)
print(f"Missing dependency: '{package}' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
raise SystemExit(2)
@@ -31,7 +31,7 @@ def main() -> int:
try:
from pypdf import PdfReader, PdfWriter
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
print("Missing dependency: 'pypdf' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
reader = PdfReader(args.pdf)
+1 -1
View File
@@ -51,7 +51,7 @@ def main() -> int:
try:
from pypdf import PdfReader, PdfWriter
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
print("Missing dependency: 'pypdf' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
reader = PdfReader(args.pdf)
+1 -1
View File
@@ -103,7 +103,7 @@ def main() -> int:
try:
from pypdf import PdfReader, PdfWriter
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf reportlab'",
print("Missing dependency: 'pypdf reportlab' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter",
file=sys.stderr)
return 2
@@ -26,7 +26,7 @@ def main() -> int:
try:
from pypdf import PdfReader, PdfWriter
except ImportError:
print("Missing dependency: install with 'python3 -m pip install pypdf'", file=sys.stderr)
print("Missing dependency: 'pypdf' is not importable here; install it into the persistent documents environment (pdf skill, Prerequisites) and rerun with that interpreter", file=sys.stderr)
return 2
reader = PdfReader(args.pdf)
+21 -3
View File
@@ -33,15 +33,33 @@ and slide rendering — all offline, no PowerPoint installation required.
## Prerequisites
- Python 3.10+ with `python-pptx` installed
(`pip install python-pptx`).
- Python 3.10+ with `python-pptx` (import name `pptx`).
- Pick the interpreter with `terminal`. Use `python` when it already imports
the libraries (the Hermes Docker image ships them); otherwise use a
persistent side environment under the Hermes home, which survives container
restarts and never touches the Hermes install itself:
```bash
export PY=python
if ! "$PY" -c "import pptx" 2>/dev/null; then
DOCS_ENV="${HERMES_HOME:-$HOME/.hermes}/environments/documents"
PY="$DOCS_ENV/bin/python"
"$PY" -c "" 2>/dev/null || python -m venv --clear "$DOCS_ENV" # new, or broken by a Python upgrade
"$PY" -c "import pptx" 2>/dev/null || "$PY" -m pip install -q python-pptx
fi
"$PY" scripts/pptx_create.py --help
```
Run every helper below with `"$PY"`. On Windows the side interpreter is
`%HERMES_HOME%\environments\documents\Scripts\python.exe`. Never
`pip install` into the interpreter that runs Hermes.
- Optional: LibreOffice (`soffice`) plus poppler (`pdftoppm` or
`pdftocairo`) for rendering slides to PNGs and for PDF export.
`pptx_render.py` detects both with `shutil.which` and degrades
gracefully (reports `{"rendered": false, "missing": [...]}`, exit 0)
when absent — all create/read/edit operations work without them.
- Check availability via `terminal`:
`python -c "import pptx; print(pptx.__version__)"` and `which soffice pdftoppm`.
`"$PY" -c "import pptx; print(pptx.__version__)"` and `which soffice pdftoppm`.
## How to Run
+21 -2
View File
@@ -38,8 +38,27 @@ scripts are argparse CLIs that print JSON and use explicit UTF-8 I/O.
## Prerequisites
- Python 3.10+ with `openpyxl` (`pip install openpyxl`). No other
third-party packages are needed; everything else is stdlib.
- Python 3.10+ with `openpyxl`. No other third-party packages are
needed; everything else is stdlib.
- Pick the interpreter with `terminal`. Use `python` when it already imports
the libraries (the Hermes Docker image ships them); otherwise use a
persistent side environment under the Hermes home, which survives container
restarts and never touches the Hermes install itself:
```bash
export PY=python
if ! "$PY" -c "import openpyxl" 2>/dev/null; then
DOCS_ENV="${HERMES_HOME:-$HOME/.hermes}/environments/documents"
PY="$DOCS_ENV/bin/python"
"$PY" -c "" 2>/dev/null || python -m venv --clear "$DOCS_ENV" # new, or broken by a Python upgrade
"$PY" -c "import openpyxl" 2>/dev/null || "$PY" -m pip install -q openpyxl
fi
"$PY" scripts/xlsx_create.py --help
```
Run every helper below with `"$PY"`. On Windows the side interpreter is
`%HERMES_HOME%\environments\documents\Scripts\python.exe`. Never
`pip install` into the interpreter that runs Hermes.
- Optional: LibreOffice (`soffice`) for headless recalculation or
format conversion.
Generated
+93 -1
View File
@@ -1738,6 +1738,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/29/64/0b75bc50ec53b4e000bac913625511215aa96124adf5dba8c4baa17c02cd/espeakng_loader-0.2.4-py3-none-win_arm64.whl", hash = "sha256:d7a2928843eaeb2df82f99a370f44e8a630f59b02f9b0d1f168a03c4eeb76b89", size = 9426841, upload-time = "2025-01-17T01:23:21.766Z" },
]
[[package]]
name = "et-xmlfile"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" },
]
[[package]]
name = "eval-type-backport"
version = "0.4.0"
@@ -2394,6 +2403,13 @@ discord = [
doc-extract = [
{ name = "firecrawl-anydoc" },
]
documents = [
{ name = "openpyxl" },
{ name = "pypdf" },
{ name = "python-docx" },
{ name = "python-pptx" },
{ name = "reportlab" },
]
edge-tts = [
{ name = "edge-tts" },
]
@@ -2673,6 +2689,7 @@ requires-dist = [
{ name = "numpy", marker = "extra == 'voice'", specifier = "==2.5.4" },
{ name = "numpy", marker = "extra == 'wake'", specifier = "==2.5.4" },
{ name = "openai", marker = "python_full_version >= '3.14'", specifier = "==2.54.0" },
{ name = "openpyxl", marker = "extra == 'documents'", specifier = "==3.1.5" },
{ name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'otlp'", specifier = "==1.45.1" },
{ name = "opentelemetry-sdk", marker = "extra == 'otlp'", specifier = "==1.45.1" },
{ name = "packaging", marker = "python_full_version >= '3.14'", specifier = "==26.3" },
@@ -2694,11 +2711,14 @@ requires-dist = [
{ name = "pyjwt", extras = ["crypto"], marker = "python_full_version >= '3.14'", specifier = "==2.15.1" },
{ name = "pyopen-wakeword", marker = "(platform_machine != 'ARM64' and platform_machine != 'x86_64' and extra == 'wake') or (platform_machine == 'ARM64' and sys_platform == 'darwin' and extra == 'wake') or (platform_machine == 'x86_64' and sys_platform == 'win32' and extra == 'wake') or (sys_platform != 'darwin' and sys_platform != 'win32' and extra == 'wake')", specifier = "==1.1.0" },
{ name = "pyopen-wakeword", marker = "(platform_machine != 'ARM64' and platform_machine != 'x86_64' and extra == 'wake-openwakeword') or (platform_machine == 'ARM64' and sys_platform == 'darwin' and extra == 'wake-openwakeword') or (platform_machine == 'x86_64' and sys_platform == 'win32' and extra == 'wake-openwakeword') or (sys_platform != 'darwin' and sys_platform != 'win32' and extra == 'wake-openwakeword')", specifier = "==1.1.0" },
{ name = "pypdf", marker = "extra == 'documents'", specifier = "==6.21.2" },
{ name = "pypinyin", marker = "extra == 'wake'", specifier = "==0.55.0" },
{ name = "pypinyin", marker = "extra == 'wake-sherpa'", specifier = "==0.55.0" },
{ name = "python-docx", marker = "extra == 'documents'", specifier = "==1.2.0" },
{ name = "python-dotenv", marker = "python_full_version >= '3.14'", specifier = "==1.2.4" },
{ name = "python-multipart", marker = "python_full_version >= '3.14'", specifier = ">=0.0.9,<1" },
{ name = "python-multipart", marker = "extra == 'web'", specifier = "==0.0.32" },
{ name = "python-pptx", marker = "extra == 'documents'", specifier = "==1.0.2" },
{ name = "python-telegram-bot", extras = ["webhooks"], marker = "extra == 'messaging'", specifier = "==22.8" },
{ name = "python-telegram-bot", extras = ["webhooks"], marker = "extra == 'telegram'", specifier = "==22.8" },
{ name = "python-telegram-bot", extras = ["webhooks"], marker = "extra == 'termux'", specifier = "==22.8" },
@@ -2707,6 +2727,7 @@ requires-dist = [
{ name = "qrcode", marker = "extra == 'dingtalk'", specifier = "==7.4.2" },
{ name = "qrcode", marker = "extra == 'feishu'", specifier = "==7.4.2" },
{ name = "qrcode", marker = "extra == 'messaging'", specifier = "==7.4.2" },
{ name = "reportlab", marker = "extra == 'documents'", specifier = "==5.1.0" },
{ name = "requests", marker = "python_full_version >= '3.14'", specifier = "==2.34.2" },
{ name = "resvg-py", marker = "python_full_version >= '3.14'", specifier = "==0.5.0" },
{ name = "rich", marker = "python_full_version >= '3.14'", specifier = "==14.3.4" },
@@ -2748,7 +2769,7 @@ requires-dist = [
{ name = "winrt-windows-services-store", marker = "python_full_version >= '3.14' and sys_platform == 'win32'", specifier = ">=3.2.1,<4" },
{ name = "youtube-transcript-api", marker = "extra == 'youtube'", specifier = "==1.2.4" },
]
provides-extras = ["uvloop", "anthropic", "exa", "firecrawl", "parallel-web", "ddgs", "fal", "edge-tts", "neutts", "kittentts", "piper", "modal", "daytona", "vercel", "google-meet", "messaging", "cron", "slack", "matrix", "wecom", "keychain", "tts-premium", "voice", "wake", "telegram", "discord", "stt-whisper", "audio-io", "silk", "wake-openwakeword", "wake-sherpa", "wake-porcupine", "google-chat", "doc-extract", "trace-upload", "vision", "pty", "mcp", "nemo-relay", "sms", "teams", "computer-use", "acp", "mistral", "otlp", "bedrock", "vertex", "azure-identity", "termux", "termux-all", "dingtalk", "feishu", "google", "youtube", "web", "all"]
provides-extras = ["uvloop", "anthropic", "exa", "firecrawl", "parallel-web", "ddgs", "fal", "edge-tts", "neutts", "kittentts", "piper", "modal", "daytona", "vercel", "google-meet", "messaging", "cron", "slack", "matrix", "wecom", "keychain", "tts-premium", "voice", "wake", "telegram", "discord", "stt-whisper", "audio-io", "silk", "wake-openwakeword", "wake-sherpa", "wake-porcupine", "google-chat", "doc-extract", "documents", "trace-upload", "vision", "pty", "mcp", "nemo-relay", "sms", "teams", "computer-use", "acp", "mistral", "otlp", "bedrock", "vertex", "azure-identity", "termux", "termux-all", "dingtalk", "feishu", "google", "youtube", "web", "all"]
[package.metadata.requires-dev]
dev = [
@@ -4330,6 +4351,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/64/a8/bb76c7356de8ad57f59d5ff993d434df0607f07f08bcc9c9a5c275e399c0/openai-2.54.0-py3-none-any.whl", hash = "sha256:89089789197ccdb87f173a03145ed1598d00795220c93e96cf712b1cbf5e5f2b", size = 1660351, upload-time = "2026-08-11T18:46:56.684Z" },
]
[[package]]
name = "openpyxl"
version = "3.1.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "et-xmlfile" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" },
]
[[package]]
name = "opentelemetry-api"
version = "1.45.1"
@@ -5324,6 +5357,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl", hash = "sha256:ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4", size = 126420, upload-time = "2026-09-20T20:59:04.025Z" },
]
[[package]]
name = "pypdf"
version = "6.21.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/06/28/081f1cbcca7042441dafc8de00f48ba60853f89e5bff92c5f26477346d81/pypdf-6.21.2.tar.gz", hash = "sha256:0a3e1c5c886a1be03646c16a04827cc166c2a0575c971b26a4bf89686551c2d4", size = 7144571, upload-time = "2026-10-21T16:44:55.272Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1b/4d/c9d2d67f204eff463237a1a8f5e25a6923b7b1043982b91fcaf104c8ef60/pypdf-6.21.2-py3-none-any.whl", hash = "sha256:1d44d1a3a200f25062adff0085b02040e4646431ac2ccac22e42679e91466084", size = 407766, upload-time = "2026-10-21T16:44:53.626Z" },
]
[[package]]
name = "pypinyin"
version = "0.55.0"
@@ -5382,6 +5424,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "python-docx"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "lxml" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256, upload-time = "2025-06-16T20:46:27.921Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987, upload-time = "2025-06-16T20:46:22.506Z" },
]
[[package]]
name = "python-dotenv"
version = "1.2.4"
@@ -5429,6 +5484,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/93/f6729f10149305262194774d6c8b438c0b084740cf239f48ab97b4df02fa/python_olm-3.2.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a5e68a2f4b5a2bfa5fdb5dbfa22396a551730df6c4a572235acaa96e997d3f", size = 297000, upload-time = "2023-11-28T19:25:31.045Z" },
]
[[package]]
name = "python-pptx"
version = "1.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "lxml" },
{ name = "pillow" },
{ name = "typing-extensions" },
{ name = "xlsxwriter" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/a9/0c0db8d37b2b8a645666f7fd8accea4c6224e013c42b1d5c17c93590cd06/python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095", size = 10109297, upload-time = "2024-08-07T17:33:37.772Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba", size = 472788, upload-time = "2024-08-07T17:33:28.192Z" },
]
[[package]]
name = "python-socketio"
version = "5.17.0"
@@ -5706,6 +5776,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/01/aa/074e2cfb3d8101a6a764aba5f7c5d1e21de087483e35bdc0c4ce2eb60364/regex-2026.9.29-cp314-cp314t-win_arm64.whl", hash = "sha256:9173db3be74a35cb6731701094b98120f7ee4876a287882a59cdea1fa7da342f", size = 285954, upload-time = "2026-09-29T00:48:38.901Z" },
]
[[package]]
name = "reportlab"
version = "5.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "charset-normalizer" },
{ name = "pillow" },
]
sdist = { url = "https://files.pythonhosted.org/packages/89/22/c18474be40b9bbb52fdb7051177d4d25f1ea59fada17eb181026411e93b1/reportlab-5.1.0.tar.gz", hash = "sha256:6878200464527e87672d19e9f7cfc6bb890e3a6931e74ac6a1a8d1754aa55671", size = 4109612, upload-time = "2026-10-22T07:26:43.307Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/3c/bf68d44f160b44685fc714f746a954c71d0091b3dc4fd3c8670154b44841/reportlab-5.1.0-py3-none-any.whl", hash = "sha256:59fdbaec5efe437095ad1d7292ec9a271c8ca2d3ec75e889c6e61400929b90df", size = 2018647, upload-time = "2026-10-22T07:26:40.754Z" },
]
[[package]]
name = "requests"
version = "2.34.2"
@@ -7468,6 +7551,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" },
]
[[package]]
name = "xlsxwriter"
version = "3.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/93/f2/98b45e52bc0330256bd4bb039cc44b28f838ad85602ce4f358f355d501bb/xlsxwriter-3.3.0.tar.gz", hash = "sha256:0a6bf2e4dcec8b29dd9b86d1e622132134c48bb6fd10f925fdfb229f0447199a", size = 221380, upload-time = "2026-10-22T00:26:03.317Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/9d/3d30eaaa7e06bdb6172f938b2661d64f8d899bdca54120384ecf3759488a/xlsxwriter-3.3.0-py3-none-any.whl", hash = "sha256:5f04772d43281da5109af66f39b65153359a203e896847c416476e709b024469", size = 179548, upload-time = "2026-10-22T00:26:02.009Z" },
]
[[package]]
name = "yarl"
version = "1.25.1"