"""Filesystem access to a vault, confined to its root. `resolve_in_root` is the single place that turns a vault-relative path into an absolute one and refuses empty, absolute or escaping values, so no tool can reach outside the vault. `read_note_payload` returns the `read_note` payload shape shared by the stdio server and the connector: UTF-8 text inline, or a binary descriptor (size, sha256, hint) for captured originals under `raw/`. """ from __future__ import annotations import hashlib from pathlib import Path from typing import Any, Dict def resolve_in_root(root: Path, raw_path: Any, *, label: str = "path") -> Path: """Turn a vault-relative path into an absolute path inside `root`.""" value = str(raw_path or "").strip() if not value: raise ValueError(f"'{label}' is required") candidate = Path(value) if candidate.is_absolute(): raise ValueError(f"'{label}' must be vault-relative, got absolute '{value}'") root = Path(root).resolve() target = (root / candidate).resolve() if target != root and not target.is_relative_to(root): raise ValueError(f"'{label}' resolves outside repository root: '{value}'") return target def relative_to_root(root: Path, target: Path) -> str: return target.relative_to(Path(root).resolve()).as_posix() def read_note_payload(root: Path, raw_path: Any) -> Dict[str, Any]: """The `read_note` payload for a vault-relative path.""" target = resolve_in_root(root, raw_path) if not target.exists(): raise FileNotFoundError(f"note not found at {target}") relative = relative_to_root(root, target) data = target.read_bytes() try: text = data.decode("utf-8") except UnicodeDecodeError: return { "path": relative, "binary": True, "bytes": len(data), "sha256": hashlib.sha256(data).hexdigest(), "hint": ( f"{target} is not UTF-8 text (e.g. a PDF or image captured under " "raw/). Open it with a file-reading tool that handles binaries; " "this server returns text only." ), } return {"path": relative, "binary": False, "content": text}