Add isToolAuditEnabled() helper that checks the auditToolOperations
DB setting (off by default) or falls back to the enterprise
audit_export feature flag. The createToolRoute factory now emits a
TOOL_EXECUTED audit entry on successful tool execution when enabled,
using a fire-and-forget pattern so a failed audit write never blocks
the tool response.
The home-page preview panel always rendered ImageViewer regardless of the
uploaded file's modality, causing videos, audio, PDFs, and data files to
show "Preview not available". Root cause: the preview branching only
checked for a blob URL and assumed all files were images.
Fix: branch on currentEntry.previewKind so the existing MediaPlayerView,
DocumentView, and a file-info fallback are activated for their respective
modalities. Also widen the AppLayout dropzone from image-only to all file
types so non-image files can reach the home page in the first place.
Add horizontal filter tabs (All, Image, Video, Audio, Documents, Files)
above the search bar so users can instantly scope the 157-tool catalog to
one modality. Active tab uses the modality accent color. In "all" mode
file-modality tools remain merged into the document section; selecting a
specific modality tab shows only that modality with its own header.
Add a row of filter tabs (All, Image, Video, Audio, Documents,
Data & Files) at the top of the fullscreen tool grid. Selecting a
tab filters the visible tools by modality, and the existing search
still works within the filtered set.
Also registers the Wand2 icon needed by the new video-effects
category header.
Static data-driven test covering 90 tools with 551 variations. For each
tool that has configurable settings, every enum value, boundary number,
and boolean flag is exercised through the HTTP endpoint. The test asserts
no 500s occur and validates response shape for 200/202 results.
Coverage spans image (29 tools), video (20), audio (15), PDF/document
(21), and data (5) modalities.
Edge worker 301-redirects www.snapotter.com to snapotter.com to stop
Google from splitting domain authority. Adds X-Robots-Tag: noindex on
/_next/static/ and /_next/data/ paths to reclaim crawl budget wasted
on JS chunks. Adds snapotter.com website badge to README so the
highest-authority backlink (GitHub) points to the main site.
The refactored CTE used = ANY(${validIds}::uuid[]) which Drizzle
serializes as a scalar text parameter with a ::uuid[] cast. Since
the user_files.id column is text (not uuid), PostgreSQL rejects
the text = uuid comparison. Replace with IN (...) via sql.join()
to pass each ID as a plain text parameter matching the column type.
Closes#71. Several error paths in the worker could leak internal
filesystem paths (/tmp/workspace, /data/ai/venv, /app) through SSE
frames, resultPayload objects, and Redis batch-error lists. The
existing stripInternalPaths call at worker.ts line 345 only covered
the single-file processToolJob catch block.
Wrapped 6 additional call sites:
- processPipelineStep: prevError from DB and catch errorMsg
- processPipelineFinalize: composed errorMsg reaching SSE,
recordChildOutcome, and resultPayload
- processBatchChild: catch error reaching recordChildOutcome
and resultPayload
- processBatchFinalize: manifest errorMsg from DB rows
- recordChildOutcome (batch-progress.ts): defense-in-depth
strip before Redis rpush
Added 10 unit tests for stripInternalPaths covering /tmp, /data,
/app, /opt, /home, /workspace, multi-path messages, safe passthrough,
and pipeline-step wrapping.
On 1.x-to-2.0 upgrade the /data/ai/venv already existed with stale
packages, so the entrypoint skipped the cp from /opt/venv and 2.0
Python tools failed with ImportError (#85).
Dockerfile now writes a SHA-256 of `pip freeze` to /opt/venv/.venv-version
at build time. The entrypoint compares that stamp against the copy on the
volume; on mismatch it does a clean nuke + re-copy and resets
installed.json so the UI correctly shows bundles as needing reinstall.
Models in /data/ai/models survive the refresh, so bundle reinstall only
reruns pip (model downloads are idempotent).
Backward compatible: if the image has no stamp (old build), the
entrypoint falls through to the existing skip-if-exists behavior.