--- title: "External integrations" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{External integrations} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` `zot` has three boundaries beyond ordinary in-memory planning: the local Zotero SQLite database, the optional semantic sidecar, and Zotero file storage. Their dependencies and effects are intentionally different. ```{r} library(data.table) library(zot) ``` | Boundary | Reads or effects | Operational note | |---|---|---| | Local SQLite | Read-only local query | May lag synchronized API state | | Semantic sidecar | Read-only Chroma query through Python | Depends on an existing index | | File attachment | Creates an item and may upload bytes | Has immediate API and storage effects | ## Query SQLite read-only `zotLocalQuery()` opens the supplied database with SQLite's read-only flag and closes the connection when the query returns. This small fixture demonstrates the boundary without depending on an installed Zotero client. ```{r} Path <- tempfile(fileext = ".sqlite") Connection <- DBI::dbConnect(RSQLite::SQLite(), Path) DBI::dbWriteTable( Connection, "items", data.frame(key = c("A", "B"), title = c("One", "Two")) ) DBI::dbDisconnect(Connection) zotLocalQuery( "SELECT key, title FROM items WHERE key = ?", params = list("B"), path = Path ) unlink(Path) ``` `zotLocalItems()` knows Zotero's client schema and returns non-trashed regular items with library and date fields: ```{r, eval = FALSE} Items <- zotLocalItems(library = "user") Items[, .(key, itemType, title, dateModified)] ``` The desktop client may be in WAL mode or awaiting synchronization. These rows are useful for a scan, not for proving a remote write succeeded. ## Query the optional semantic sidecar `ztSemantic()` launches the Python interpreter installed with `zotero-mcp-server` and queries its `zotero_library` Chroma collection. The defaults target one common Unix `uv` installation; other layouts must provide `pythonPath` and `dbPath` explicitly. ```{r, eval = FALSE} Results <- ztSemantic( "conditional mean spectrum ground motion selection", limit = 5L ) Results[, .(itemKey, title, distance)] ``` The function does not install Python, create the Chroma database, or run `zotero-mcp update-db`. Refreshing the index is a separate operation, and its results can lag current API data. The current query has no library selector: `limit` applies across all indexed personal and group records, and `groupID` is returned so callers can inspect their origin. ## Exercise the attachment protocol offline `ztAttach()` creates an attachment item, requests storage authorization, uploads bytes when needed, registers the upload, and verifies the server-side MD5. The example supplies a fake API performer and a transport that reports an identical file already present, so no bytes leave the process. ```{r} File <- tempfile(fileext = ".pdf") writeBin(as.raw(1:8), File) Performer <- function(config, method, path, query, body, version) { list( status = 200L, headers = list(), body = list(successful = list(`0` = list(key = "ATT1"))) ) } Config <- zotConfig( userID = "42", key = "example-key", performer = Performer ) Transport <- list( authorize = function(...) list(exists = 1L), upload = function(...) stop("upload should not run"), register = function(...) stop("registration should not run") ) ztAttach( Config, parentKey = NA_character_, filePath = File, fileTransport = Transport ) unlink(File) ``` With the default transport, this call has immediate external effects and may consume Zotero storage quota. A file-bearing record is refused by the block-move planner because that planner does not transfer attachment bytes. ## Keep evidence in the correct plane A curation workflow may discover candidates through SQLite or semantic search, establish current versions through the API, and upload an attachment. The same functions can be invoked interactively, from scripts, or by agent applications. API reads are required to observe remote outcomes; local data sources can lag the synchronized library.