feat: scaffold inicial del plugin de Obsidian osint_obsidian_plugin
Plugin fino (id osint-db) que habla HTTP con el service local osint_db (FastAPI + DuckDB) y renderiza tablas de datos en las notas del vault osint mediante el code block osintdb. Incluye parser puro de directivas con tests (node --test), settings tab, comando de paleta, enlaces internos para columnas note_path, build con esbuild + tsc y deploy.sh al vault. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
---
|
||||
name: osint_obsidian_plugin
|
||||
lang: ts
|
||||
domain: osint
|
||||
version: 0.1.0
|
||||
description: "Plugin de Obsidian (id osint-db) para el vault osint: ejecuta queries SQL y queries nombradas contra el service local osint_db (FastAPI + DuckDB) via HTTP y renderiza tablas de datos dentro de las notas mediante el code block osintdb. Plugin fino: no embebe ninguna base de datos."
|
||||
tags: [osint, obsidian, plugin, duckdb]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
framework: "obsidian-plugin"
|
||||
entry_point: "main.ts"
|
||||
dir_path: "projects/osint/apps/osint_obsidian_plugin"
|
||||
repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/osint_obsidian_plugin"
|
||||
e2e_checks:
|
||||
- id: build
|
||||
cmd: "pnpm install --frozen-lockfile=false && pnpm build"
|
||||
timeout_s: 300
|
||||
- id: artifact
|
||||
cmd: "test -f main.js"
|
||||
- id: tests
|
||||
cmd: "pnpm test"
|
||||
timeout_s: 60
|
||||
---
|
||||
|
||||
# osint_obsidian_plugin
|
||||
|
||||
Plugin de Obsidian para el vault `~/Obsidian/osint`. Es deliberadamente fino: no embebe
|
||||
DuckDB ni ninguna otra base de datos. Toda la lógica de datos vive en el service local
|
||||
`osint_db` (FastAPI, dueño de la DuckDB con datos OSINT), con el que el plugin habla por
|
||||
HTTP usando `requestUrl` de la API de Obsidian (evita CORS; nunca `fetch`).
|
||||
|
||||
## Qué hace
|
||||
|
||||
1. **Code block `osintdb`**: dentro de cualquier nota, un bloque de código con lenguaje
|
||||
`osintdb` se renderiza como una tabla de datos. Dos formas de contenido:
|
||||
- SQL crudo (el bloque entero es un SELECT, va a `POST /api/query`).
|
||||
- Directivas `clave: valor` al inicio del bloque: `query` (nombre de query guardada,
|
||||
va a `POST /api/query/named`), `max_rows` y `title`.
|
||||
2. **Render seguro**: tabla HTML construida con `createEl` (los valores se insertan como
|
||||
texto, nunca como HTML crudo), header sticky, conteo de filas y nota "(truncado)"
|
||||
cuando el service recorta el resultado. Errores del service y service caído se
|
||||
muestran con mensajes claros, incluido el comando para arrancar `osint_db`.
|
||||
3. **Botón "Refrescar"** por bloque: re-ejecuta la query sin recargar la nota.
|
||||
4. **Enlaces a notas**: si una columna se llama `note_path` y el valor termina en `.md`,
|
||||
la celda se renderiza como enlace interno de Obsidian que abre la nota al hacer clic.
|
||||
5. **Settings tab**: URL base del service (default `http://127.0.0.1:8771`) y `max_rows`
|
||||
por defecto (500), persistidos con `loadData`/`saveData`.
|
||||
6. **Comando de paleta** "OSINT DB: Insertar bloque de query": inserta una plantilla de
|
||||
bloque `osintdb` en el editor.
|
||||
|
||||
## Ejemplo de bloque
|
||||
|
||||
Forma con SQL crudo:
|
||||
|
||||
```osintdb
|
||||
title: Personas del caso X
|
||||
max_rows: 100
|
||||
SELECT nombre, contexto, note_path FROM main.personas WHERE contexto = 'caso_x';
|
||||
```
|
||||
|
||||
Forma con query nombrada (definida en el service):
|
||||
|
||||
```osintdb
|
||||
query: personas_por_contexto
|
||||
max_rows: 100
|
||||
```
|
||||
|
||||
## Contrato API del service osint_db
|
||||
|
||||
Base URL por defecto: `http://127.0.0.1:8771`. El service responde siempre HTTP 200 y el
|
||||
plugin decide por el campo `status` del body.
|
||||
|
||||
- `GET /api/health` → `{"status":"ok","db_path":"...","tables":N}`
|
||||
- `GET /api/tables` → listado de tablas con schema, kind, row_count y columnas.
|
||||
- `POST /api/query` body `{"sql":"SELECT ...","params":[],"max_rows":500}` →
|
||||
`{"status":"ok","columns":[...],"rows":[...],"row_count":N,"truncated":bool}` o
|
||||
`{"status":"error","error":"..."}`.
|
||||
- `GET /api/queries` → queries nombradas disponibles.
|
||||
- `POST /api/query/named` body `{"name":"...","max_rows":500}` → misma shape que `/api/query`.
|
||||
|
||||
## Cómo construir
|
||||
|
||||
```bash
|
||||
cd projects/osint/apps/osint_obsidian_plugin
|
||||
pnpm install
|
||||
pnpm build # tsc --noEmit + esbuild -> main.js
|
||||
pnpm test # tests del parser de bloques (node --test, sin Obsidian)
|
||||
```
|
||||
|
||||
Requiere Node >= 23 para los tests (usa el type stripping nativo de `node --test` sobre
|
||||
archivos `.ts`).
|
||||
|
||||
## Cómo desplegar
|
||||
|
||||
```bash
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
Copia `manifest.json`, `main.js` y `styles.css` a
|
||||
`/home/enmanuel/Obsidian/osint/.obsidian/plugins/osint-db/`.
|
||||
|
||||
**Activación manual (la hace el humano, no un agente):** tras el deploy, abrir Obsidian →
|
||||
Settings → Community plugins → activar "OSINT DB". Si Obsidian ya estaba abierto, hace
|
||||
falta recargar la app (Ctrl+R) o reiniciarla para que detecte el plugin nuevo o la
|
||||
versión nueva de `main.js`.
|
||||
|
||||
## Estructura
|
||||
|
||||
- `main.ts` — plugin completo (processor, render, settings, comando).
|
||||
- `parse.ts` — parser puro del contenido de los bloques `osintdb` (testeable sin Obsidian).
|
||||
- `tests/parse.test.ts` — tests del parser con `node --test`.
|
||||
- `esbuild.config.mjs` — bundle CommonJS a `main.js` con `obsidian` como external.
|
||||
- `deploy.sh` — copia los artefactos al vault.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- El plugin no arranca el service: si `osint_db` no está corriendo, los bloques muestran
|
||||
"osint_db no responde en <url>" con el comando de arranque como hint.
|
||||
- `main.js` es artefacto de build (gitignored): siempre `pnpm build` antes de `deploy.sh`.
|
||||
- `isDesktopOnly: true` en el manifest — el service es local, no tiene sentido en móvil.
|
||||
Reference in New Issue
Block a user