Files
hermes-agent/tools/binary_extensions.py
kshitijk4poor c6f3a3cf24 refactor(tools): one suffix helper for the binary-extension checks; drop the dead .db3 member
_has_extension_in and is_sqlite_sidecar each re-implemented the same
rfind('.') / -1 / .lower() suffix extraction, and the write guard used a
third spelling (filepath[filepath.rfind("."):]) for its refusal messages
next to os.path.splitext in the overwrite branch. Add _lower_suffix()
("" when no dot) and route both predicates through it; the write guard
now uses os.path.splitext for every message's displayed extension.

_SQLITE_EXTENSIONS was `{.db,.sqlite,.sqlite3,.db3} & BINARY_EXTENSIONS`,
which silently dropped .db3 (not a binary extension anywhere in the
codebase, so `x.db3-wal` was never a sidecar). Spell the set as the three
members that actually take effect and assert the subset invariant instead
of hiding it behind an intersection. Behaviour is unchanged.
2026-09-17 21:17:26 +05:30

87 lines
3.9 KiB
Python

"""Binary file extensions to skip for text-based operations (ported from
free-code src/constants/files.ts)."""
# Images, video, audio, archives, executables, documents (.pdf deliberately
# excluded — text-based, agents may want to inspect), fonts, bytecode/VM,
# databases, design/3D, Flash, lock/profiling data.
BINARY_EXTENSIONS = frozenset({
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".webp", ".tiff", ".tif",
".mp4", ".mov", ".avi", ".mkv", ".webm", ".wmv", ".flv", ".m4v", ".mpeg", ".mpg",
".mp3", ".wav", ".ogg", ".flac", ".aac", ".m4a", ".wma", ".aiff", ".opus",
".zip", ".tar", ".gz", ".bz2", ".7z", ".rar", ".xz", ".z", ".tgz", ".iso",
".exe", ".dll", ".so", ".dylib", ".bin", ".o", ".a", ".obj", ".lib", ".app", ".msi", ".deb", ".rpm",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".odt", ".ods", ".odp",
".ttf", ".otf", ".woff", ".woff2", ".eot",
".pyc", ".pyo", ".class", ".jar", ".war", ".ear", ".node", ".wasm", ".rlib",
".sqlite", ".sqlite3", ".db", ".mdb", ".idx",
".psd", ".ai", ".eps", ".sketch", ".fig", ".xd", ".blend", ".3ds", ".max",
".swf", ".fla", ".lockb", ".dat", ".data",
})
# Container documents (OOXML/ODF/EPUB zips, OLE, RTF) a plain-text write can
# NEVER produce validly: read_file auto-extracts them, so writing the text back
# via write_file/patch silently destroys the document. PDF is deliberately
# absent — raw PDF syntax is text-authorable, so only overwrites are dangerous
# (the write guard handles that via is_pdf_path).
OPAQUE_DOCUMENT_EXTENSIONS = frozenset({
".doc", ".docx", ".docm", ".xls", ".xlsx", ".xlsm", ".xlsb",
".ppt", ".pps", ".pot", ".pptx", ".pptm", ".ppsx", ".ppsm",
".odt", ".ods", ".odp", ".rtf", ".epub",
})
# SQLite journal sidecars (``x.db-wal``, ``x.sqlite3-shm``, ``x.db-journal``)
# hang their marker off the database's own extension, so the final ``.suffix``
# is ".db-wal" — never in any extension set — and both the read guard and the
# write guard would treat the raw page bytes as text.
_SQLITE_SIDECAR_MARKERS = ("-wal", "-shm", "-journal")
# Only these suffixes take a sidecar marker; ``report.docx-wal`` is not a
# document and must not be treated as one.
_SQLITE_EXTENSIONS = frozenset({".db", ".sqlite", ".sqlite3"})
assert _SQLITE_EXTENSIONS <= BINARY_EXTENSIONS
def _lower_suffix(path: str) -> str:
"""Lower-cased final ``.suffix`` of ``path`` ("" when there is no dot)."""
dot = path.rfind(".")
return "" if dot == -1 else path[dot:].lower()
def _strip_sidecar_marker(suffix: str) -> str | None:
"""Return the database suffix a lower-cased sidecar suffix hangs off
(``.db-wal`` -> ``.db``), or None when ``suffix`` is not a SQLite sidecar."""
for marker in _SQLITE_SIDECAR_MARKERS:
if suffix.endswith(marker):
base = suffix[: -len(marker)]
if base in _SQLITE_EXTENSIONS:
return base
return None
def _has_extension_in(path: str, extensions: frozenset) -> bool:
"""Case-insensitive check on the final ``.suffix``; pure string, no I/O.
A SQLite sidecar counts as its database's extension (only SQLite suffixes
are stripped, so ``x.docx-wal`` stays unrecognised)."""
suffix = _lower_suffix(path)
return (_strip_sidecar_marker(suffix) or suffix) in extensions
def is_sqlite_sidecar(path: str) -> bool:
"""True for ``x.db-wal`` / ``x.sqlite3-shm`` / ``x.db-journal`` paths.
Pure string, no I/O — a sidecar path is never a legitimate text target
even when no sidecar exists on disk (a checkpointed db has none)."""
return _strip_sidecar_marker(_lower_suffix(path)) is not None
def has_binary_extension(path: str) -> bool:
return _has_extension_in(path, BINARY_EXTENSIONS)
def has_opaque_document_extension(path: str) -> bool:
return _has_extension_in(path, OPAQUE_DOCUMENT_EXTENSIONS)
def is_pdf_path(path: str) -> bool:
return path.lower().endswith(".pdf")