Commit graph

17 commits

Author SHA1 Message Date
ced92de67c
feat: optional arborist provenance sink (opt-in, default off)
Adds neopig/arborist_sink.py: when enabled, every crawled page neopig
stores is also committed to an arborist content-addressed store — a
Merkle document_root + append-only audit chain — alongside neopig's
existing md5/FileVault storage. Verifiable dedup + FTS5 + tamper-evident
provenance; replaces nothing.

Strictly opt-in: a hard no-op unless NEOPIG_ARBORIST_ENABLED is set AND
arborist is importable. neopig behaves byte-identically without it. This
keeps neopig public-domain by default — arborist is AGPL, pulled only
when an operator opts in. neopig never touches arborist's tables; all
writes go through arborist.embed (no-raw-SQL rule preserved). Sync
SQLite writes run off the event loop via to_thread, lock-serialized,
and failures are swallowed so the mirror can never break a crawl.

Wired into NeoPig.__init__ (self.arborist_sink) and the store_page hook
in the crawl path. Phase-0 scope: page text only; media-manifest edges
are a follow-on. Tests cover disabled-by-default, flag-without-arborist,
the page->Document mapping, and enabled end-to-end + content-idempotence.
2026-05-22 13:06:59 -04:00
10382bd104
fix: fast_mode must ignore crawl-delay on robots-less sites
fast_mode is meant to ignore crawl-delay, but the per-domain delay was
only zeroed on the robots.txt-200 path. A site whose robots.txt 404s
(or errors) fell back to default_crawl_delay (2s), so under fast_mode
every concurrent fetch wave still slept ~2s — the dominant cost of a
wide crawl on a robots-less host. _enforce_crawl_delay now short-circuits
when fast_mode. Disallow unaffected; polite mode still enforces the delay.

Fixed upstream in agents.ai.unturf.com/core; this is the same one-line
fix in this copy of the verbatim-lift fetcher.
2026-05-22 09:15:22 -04:00
c8b0ecbd87 add canonical permacomputer preamble to all source files 2026-02-25 17:40:12 -05:00
58c93f217a Reorganize project structure: move modules to neopig/, scripts to scripts/, data to data/ 2026-01-06 14:06:43 -05:00
3714c9b6a9 Extract common CSS to 5 external stylesheets (DRY)
- base.css: Core layout, typography, container
- nav.css: Navigation bar styles
- forms.css: Form elements, buttons, inputs, checkboxes
- quests.css: CSS-only easter eggs
- results.css: Media grid and result card styles

Templates now link external CSS via extra_css_links block.
Page-specific styles remain inline (correct pattern).
Fixed OVER_9000 test (it's OVER 9000, so 9001).
2026-01-05 20:44:35 -05:00
c7c20c5bce Refactor serp.py into serp/ package, add /eggs endpoint
- Extract serp.py into serp/ package for better testability
  - serp/app.py: FastAPI app, LanguageMiddleware
  - serp/server.py: All route handlers
  - serp/i18n.py: Translations, LANG_NAMES with flags
  - serp/models.py: CrawlRequest model
  - serp/archive.py: Tarball handling
  - serp/logging.py: Job logging
  - serp/utils.py: slugify function
- Add /eggs endpoint listing easter eggs:
  - #0: ?lang=XX forces language via middleware
  - #1: OVER_9000 pagination limit
  - #2: The 72 Rules of St. Benedict (A Way)
- Fix test module import issue (app_module -> app_config)
- LANG_NAMES includes flags and codes (e.g., 🇺🇸 EN English)
2026-01-05 19:16:04 -05:00
7883ae0eba Refactor: Extract neopig package from neopig.py
Phase 1 of module restructuring as outlined in docs/REFACTOR.md:

New neopig/ package modules:
- live.py: Live media queue (get_live_queue, emit_live_media)
- state.py: AppendOnlyStateLog, state file helpers
- html_utils.py: trim_html_wrapper, extract_meta_from_html
- logging.py: TqdmLoggingHandler, job logging functions
- backfill/: markdown and screenshot backfill operations

Package features:
- Lazy import of NeoPig/main from neopig.py via __getattr__
- Full backwards compatibility with existing imports
- 42 new unit tests for extracted modules

Total: 458 tests passing
2026-01-05 17:30:58 -05:00
00bad59920 Fix test_database.py method names to match Database API
- upsert_page → store_page (2 occurrences)
- get_media → get_media_by_hash (2 occurrences)
- search_media → search_media_advanced
2026-01-05 14:57:49 -05:00
c351753290 Fix test_live_stream_endpoint_exists hanging on SSE request 2026-01-05 14:52:58 -05:00
8d2fbbf8b1 Fix test_stats_endpoint async context manager mock 2026-01-05 14:51:51 -05:00
a46f93aaf1 Extract i18n to module, add Georgian, translate about page body
- Extract 1100+ lines of translations from serp.py to i18n.py
- Add Georgian (ka) as 27th supported language
- Add 74 about page body text keys (hero, chapters, features, footer)
- Update about.html.j2 to use translation placeholders
- Full Chinese translations for all body text
- English fallback for other 25 languages (can be translated later)
- serp.py reduced from 3291 to 2142 lines
- i18n.py now 3808 lines with 190 keys per language
2026-01-05 10:16:48 -05:00
af50334a7b Fix tests for code media type, add source URL and lore 2026-01-02 13:41:10 -05:00
96a3c9c786 Update tests for filevault 2.0.0 API
- Rewrite test_filevault.py for new API (depth=9, hex pairs, content-addressable)
- Rewrite test_async_filevault.py to import from filevault module
- Fix test_screenshot.py defaults (enabled=True, 1024x768)
- Add backward compatibility exports to filevault.py
- All 196 tests passing
2025-12-31 16:03:43 -05:00
99c836e2c8 Unify filevault: merge storage.py into single sync+async module
- filevault.py: Vault (sync) + AsyncVault (async) in one file
- Delete storage.py (redundant async wrapper)
- Both classes share identical API: store, get, exists, delete, stats
- Add content_hash() and hash_to_path() utility functions
- Update all imports: neopig.py, serp.py, test files
- Update README with unified documentation
- 72 lines saved, cleaner architecture
2025-12-31 15:44:39 -05:00
36edc3b8dc modified: archive.py
modified:   tests/unit/test_screenshot.py
2025-12-29 11:35:14 -05:00
1847a2fee6 Add comprehensive unit tests for async modules
Tests for:
- async_web_fetcher: URL handling, media extraction, crawl modes, scoring
- domain_vault: VaultManager, HTML/Media/Linkpeek vaults
- screenshot: ScreenshotCapture configuration and availability
- storage: ImageVault with MD5 deduplication

185 tests total, all passing.
2025-12-22 20:40:58 -05:00
bc11c516b6 Add filevault system with async wrapper and convert sync ops to async
New files:
- filevault.py: Hash-based file storage with use_pairs option (v1.1.0)
- async_filevault.py: Async wrapper using asyncio.to_thread()
- domain_vault.py: Triple vault system for web archival (HTML, Media, Linkpeek)
- screenshot.py: Async screenshot capture using uri2png
- tests/unit/: Comprehensive test suite (85 tests)

Sync-to-async conversions:
- storage.py: Wrap Path operations in asyncio.to_thread()
- domain_vault.py: Wrap exists(), mkdir(), rglob(), os.walk() in asyncio.to_thread()
- screenshot.py: Wrap read_bytes(), write_bytes(), unlink() in asyncio.to_thread()

All sync filesystem operations now run in thread pool to avoid blocking async loop.
2025-12-22 20:18:20 -05:00