dsh-locator 0.7.1 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README-es.md +37 -5
  3. package/README-hi.md +35 -5
  4. package/README-pt.md +33 -5
  5. package/README.en.md +34 -4
  6. package/README.md +27 -5
  7. package/client.js +117 -17
  8. package/cordis.patch.yml +13 -0
  9. package/lib/config.d.ts +20 -0
  10. package/lib/config.d.ts.map +1 -1
  11. package/lib/config.js +38 -4
  12. package/lib/config.js.map +1 -1
  13. package/lib/content-search-worker.d.ts +8 -0
  14. package/lib/content-search-worker.d.ts.map +1 -0
  15. package/lib/content-search-worker.js +54 -0
  16. package/lib/content-search-worker.js.map +1 -0
  17. package/lib/content-search.d.ts +15 -0
  18. package/lib/content-search.d.ts.map +1 -0
  19. package/lib/content-search.js +44 -0
  20. package/lib/content-search.js.map +1 -0
  21. package/lib/content.d.ts +83 -4
  22. package/lib/content.d.ts.map +1 -1
  23. package/lib/content.js +496 -77
  24. package/lib/content.js.map +1 -1
  25. package/lib/find.d.ts +1 -1
  26. package/lib/find.d.ts.map +1 -1
  27. package/lib/find.js +8 -3
  28. package/lib/find.js.map +1 -1
  29. package/lib/http.d.ts +1 -1
  30. package/lib/http.d.ts.map +1 -1
  31. package/lib/http.js +4 -2
  32. package/lib/http.js.map +1 -1
  33. package/lib/index.d.ts +5 -1
  34. package/lib/index.d.ts.map +1 -1
  35. package/lib/index.js +106 -27
  36. package/lib/index.js.map +1 -1
  37. package/lib/reindex.d.ts +1 -1
  38. package/lib/reindex.d.ts.map +1 -1
  39. package/lib/reindex.js +2 -1
  40. package/lib/reindex.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/config.ts +58 -4
  43. package/src/content-search-worker.ts +52 -0
  44. package/src/content-search.ts +45 -0
  45. package/src/content.ts +456 -71
  46. package/src/find.ts +8 -3
  47. package/src/http.ts +4 -2
  48. package/src/index.ts +106 -25
  49. package/src/reindex.ts +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.5 — 2026-11-11
4
+
5
+ Hardening release from a full risk/bug pass against the real 3 GB content database.
6
+
7
+ * `deletePath` no longer scans every row: `docs_meta(path)` is indexed, so removing a rewritten session costs a lookup instead of ~10 ms of table scan (measured 6 ms before the index on 95k rows).
8
+ * A one- or two-character content query used to return full document bodies (up to a megabyte per hit); it now returns at most a 400-character excerpt. Matching rows are still found the same way, but the payload is bounded.
9
+ * A build interrupted by a crash, update, or forced reload is detected by the persisted `complete` flag and rebuilt on the next start instead of trusting a stale byte counter (which could let the body budget drift).
10
+ * The plugin refuses to delete a `content.db` it does not own (another tool in a shared `stateDir`); it reports an error instead of recreating the file.
11
+ * The state directory is created `0700`, `content.db` and `content.lock` are created `0600`, so indexed documents and session text are not readable by other local users.
12
+ * The build lock records the process start time as well as the pid, so a reused pid from an earlier incarnation cannot block builds for 30 minutes.
13
+ * The first lazy content query now reports `building: true` immediately (requested-start flag) instead of returning an empty page until the asynchronous boot opens the database.
14
+ * `boot()` skips a second pass when a content operation already built the database (for example `locator_reindex` right after a lazy start).
15
+
16
+ ## 0.7.4 — 2026-11-11
17
+
18
+ * The content index is **lazy by default**: plugin startup now costs nothing for content. The first body/session search (`locator_find`, the panel's “搜内容”, or `locator_reindex`) starts the build in the background; `contentLazy: false` restores the eager background build. A restart that already has a database still refreshes incrementally on first use.
19
+ * Cordis-native scheduling: the boot delay and the incremental-refresh cadence now use the base profile's timer service (`ctx.timeout`/`ctx.interval` from `@deepseek-ai/cordis-plugin-timer`), so both disposers are owned by the plugin fiber; plain timers remain as a fallback when that service is absent.
20
+ * The panel reports “indexing” and re-polls while the lazy content build runs, so the first query fills in without retyping.
21
+ * Content builds are serialized by a `<stateDir>/content.lock` (per-instance token, stale locks taken over), so a profile HMR reload or a second DSH process can no longer run two writers against the same database and drift the byte counter.
22
+ * A full content rebuild now recreates `content.db` instead of running `DROP TABLE` over multi-GB FTS tables, which had been blocking the host for about a second at the start of a rebuild.
23
+ * The body-byte budget is now persisted in `content_meta` instead of rescanned at every refresh (a 1 GB database made `SUM(length(body))` block the host for ~0.6 s each time). Deleted bodies are subtracted, and files that no longer fit are retried after stale rows are removed. The schema version moves to 2, so an existing database rebuilds once.
24
+ * The content search worker is skipped when its compiled entry is absent (TypeScript test runs) and a failed/timed-out worker falls back to the in-process query instead of returning an empty page.
25
+
26
+ ## 0.7.3 — 2026-11-11
27
+
28
+ * The content build no longer blocks the DSH host's event loop: file reads and session-log decompression are asynchronous and both build loops yield every ~8 ms (measured max event-loop gap on a 28.6 MB corpus: 968 ms → 19 ms). This is what made session loading and switching stall and could trip a WebSocket reconnect ("all sessions reloaded").
29
+ * Session logs are not chased while they are still being written: a log modified within `contentActiveSessionDelayMs` (default 120000) is indexed only after it has been quiet that long, instead of being fully decompressed and re-inserted every refresh.
30
+ * Archived sessions are skipped by default; set `contentIncludeArchived: true` to index them. Sessions deleted from disk disappear from the index on the next refresh as before.
31
+ * Lower default background load: `contentMaxFileBytes` 4 MB → 1 MB, `contentMaxTotalBytes` 2 GB → 1 GB, `contentMaxSessions` 200 → 100, `contentRefreshMs` 120 s → 300 s. All remain configurable.
32
+ * Tests: content-build event-loop cooperativeness, archived-session filtering, and active-session deferral.
33
+
34
+ ## 0.7.2 — 2026-11-10
35
+
36
+ * A restart no longer rebuilds the whole content database: the plugin reuses `content.db` and refreshes it incrementally (measured 2.9 s on a 1.3M-entry index that took 7.4 min to build from scratch). A missing, stale-schema (`user_version`) or unreadable database still falls back to a full rebuild.
37
+ * Session jumps are anchored on the Chat node key (`data-chat-node-key` embeds the indexed `messageId`), so double-clicking a session hit outlines the exact message instead of the first copy of those words in the document (often the sidebar title). When the anchor is absent the panel falls back to a phrase search restricted to the conversation transcript and pulls older pages once.
38
+ * English, Spanish, Portuguese and Hindi READMEs document content search, incremental refresh, session jumps and the `content*` configuration; the stale "no content indexing" limitation is gone.
39
+
3
40
  ## 0.7.1 — 2026-11-10
4
41
 
5
42
  * Content full builds index session messages **before** the much larger file set, so conversation search and jumps become available early.
package/README-es.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Búsqueda de archivos en todo el disco, en milisegundos, para DeepSeek Harness — sin Everything.**
6
6
 
7
- Índice propio multiplataforma (Windows USN/MFT · macOS FSEvents · Linux inotify), cuatro herramientas para el agente y un panel web ordenable.
7
+ Índice propio multiplataforma (Windows USN/MFT · macOS FSEvents · Linux inotify), seis herramientas para el agente —nombres, contenido y sesiones— y un panel web ordenable.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
@@ -62,12 +62,14 @@ Si instalas en un perfil Web ya en ejecución, **recarga la página** (`F5`): la
62
62
 
63
63
  ![Panel de búsqueda de dsh-locator: resultados Markdown filtrados por ruta, con recuento, tiempo y columnas ordenables](docs/assets/panel-en.png)
64
64
 
65
- Pulsa `Ctrl+Shift+F` en cualquier parte de la Web GUI, o haz clic en la lupa del pie de la barra lateral. El panel consulta el mismo índice mediante rutas del host del mismo origen (`POST /dsh-locator/search`, `POST /dsh-locator/status`), sin segundo servidor ni puerto extra.
65
+ Pulsa `Ctrl+Shift+F` en cualquier parte de la Web GUI, o haz clic en la lupa del pie de la barra lateral. El panel consulta el mismo índice mediante rutas del host del mismo origen (`/dsh-locator/search`, `/dsh-locator/content`, `/dsh-locator/status`, `/dsh-locator/reveal` y `/dsh-locator/open`), sin segundo servidor ni puerto extra.
66
66
 
67
67
  * Consulta vacía: entradas recientes por fecha; al escribir, debounce de 110 ms y la misma sintaxis que las herramientas.
68
68
  * Clic en una columna para ordenar por nombre, tamaño, fecha o ruta; otro clic invierte.
69
- * `Solo archivos` / `Solo carpetas`; la barra inferior muestra aciertos, límite y tiempo.
70
- * `↑`/`↓` selecciona, `Enter` copia, `Ctrl+O` abre, `Ctrl+Enter` inserta `@"ruta"` en el compositor, doble clic revela en el explorador, `Esc` cierra.
69
+ * `Solo archivos` / `Solo carpetas` filtran por tipo; la barra inferior muestra aciertos, límite y tiempo.
70
+ * Marca **Buscar contenido** para pasar de los nombres al contenido de los archivos y a los mensajes de sesión; cada acierto muestra un fragmento de una línea.
71
+ * `↑`/`↓` selecciona, `Enter` copia, `Ctrl+O` abre, `Ctrl+Enter` inserta `@"ruta"` en el compositor y `Esc` cierra.
72
+ * El doble clic sobre un archivo abre su ubicación en el gestor de archivos; el doble clic sobre un resultado de sesión abre esa conversación y resalta el mensaje encontrado por un momento.
71
73
  * La interfaz sigue `navigator.language` (zh/en/es/pt/hi).
72
74
 
73
75
  ## Herramientas
@@ -75,10 +77,24 @@ Pulsa `Ctrl+Shift+F` en cualquier parte de la Web GUI, o haz clic en la lupa del
75
77
  | Herramienta | Uso |
76
78
  |---|---|
77
79
  | `locator_search` | Consulta el índice con sintaxis estilo Everything. |
80
+ | `locator_find` | Busca en el contenido de los archivos y en los mensajes de sesión de DSH; los aciertos traen un fragmento y, cuando vienen de una conversación, `sessionId`/`messageId`. |
78
81
  | `locator_status` | Raíces, entradas, watcher, modo USN y errores. |
79
82
  | `locator_rebuild` | Reconstruye en segundo plano; las búsquedas siguen usando el índice anterior. |
83
+ | `locator_reindex` | Refresca o reconstruye el índice de contenido: incremental por defecto; `full: true` lo borra y lo vuelve a construir. |
80
84
  | `locator_export` | Copia de solo lectura en SQLite (`entries(path, name, dir, size, mtime_ms, ext)`). |
81
85
 
86
+ ### Búsqueda por contenido
87
+
88
+ `locator_find` busca en el contenido de los archivos de texto y en los mensajes de sesión de DSH mediante un índice SQLite FTS5 local. Las subcadenas en chino e inglés funcionan sin necesidad de segmentar palabras.
89
+
90
+ Cada acierto devuelve un fragmento; si viene de una conversación, incluye `sessionId` y `messageId`, de modo que el panel puede abrir esa sesión y resaltar el mensaje encontrado.
91
+
92
+ El índice de contenido es perezoso por defecto: al iniciar no se construye nada y la primera búsqueda de contenido lo arranca en segundo plano (`contentLazy: false` lo construye desde el arranque). Al reiniciar, el plugin reutiliza la base de datos existente y la actualiza de forma incremental; solo construye el índice completo (primero los registros de sesión, luego el contenido de los archivos) cuando la base no existe, tiene una versión de esquema antigua o no se puede abrir. Después, el refresco incremental se repite cada `contentRefreshMs` (5 minutos) y solo relee los archivos y las sesiones cuya `mtime` cambió; `locator_reindex` permite lanzar a mano cualquiera de los dos modos.
93
+
94
+ Mientras hay una construcción en marcha, las búsquedas siguen respondiendo con lo que ya está indexado, y la herramienta y el panel informan del estado del proceso.
95
+
96
+ Los límites actuales son 1 MB por archivo de texto, 200 000 archivos, las 100 sesiones más recientes y 1 GB de contenido en total; todos se pueden ajustar en la configuración. Las carpetas de dependencias y toolchains (`node_modules`, `.cargo`, `.bun`, `.pnpm`, `site-packages`, `toolchain`, …) se omiten por defecto — si no, se comen el presupuesto antes que tus propios documentos — y `contentExcludes` cambia esa lista. El índice lee y descomprime de forma asíncrona y cede el bucle de eventos cada ~8 ms (28,6 MB: 968 ms → 19 ms), así que no frena la carga ni el cambio de sesión; las sesiones archivadas no se indexan salvo `contentIncludeArchived: true`, y una sesión que se está escribiendo espera `contentActiveSessionDelayMs`.
97
+
82
98
  ### Sintaxis de consulta
83
99
 
84
100
  ```text
@@ -105,6 +121,18 @@ case:1 wholeword:1 mayúsculas / palabra completa
105
121
  | `windowsUsn` | Ruta rápida USN/MFT (Windows, administrador; por defecto `false`). |
106
122
  | `scanWorkers` | Lectores de directorio en paralelo; 0 = CPU. |
107
123
  | `stateDir` | Directorio de la instantánea; vacío = caché del sistema. |
124
+ | `contentIndex` | Activa la construcción del índice de contenido de archivos y mensajes de sesión (por defecto `true`). |
125
+ | `contentLazy` | Construye el índice de contenido en la primera búsqueda de contenido en vez de al iniciar; `false` lo construye en segundo plano desde el arranque (por defecto `true`). |
126
+ | `contentMaxFileBytes` | Máximo de bytes que se leen de cada archivo de texto (por defecto `1000000`). |
127
+ | `contentMaxTotalBytes` | Total de bytes de contenido que se conservan; `0` = sin límite (por defecto `1000000000`). |
128
+ | `contentMaxFiles` | Archivos de texto indexados en cada construcción; `0` = sin límite (por defecto `200000`). |
129
+ | `contentMaxSessions` | Sesiones más recientes indexadas; `0` = todas (por defecto `100`). |
130
+ | `contentRefreshMs` | Cadencia del refresco incremental de contenido; `0` lo desactiva (por defecto `300000`). |
131
+ | `contentIncludeArchived` | Indexa también las sesiones archivadas; por defecto `false`. |
132
+ | `contentActiveSessionDelayMs` | Solo indexa un registro de sesión tras estar quieto este tiempo (por defecto `120000`). |
133
+ | `contentSessionDirs` | Directorios de sesión adicionales; vacío = `$DSH_HOME/sessions` (por defecto `[]`). |
134
+ | `contentExcludes` | Tramos de ruta que la búsqueda por contenido omite (dependencias y toolchains); el índice de nombres no cambia. |
135
+
108
136
 
109
137
  ## Plataformas
110
138
 
@@ -118,6 +146,8 @@ En Windows, `scan --usn` guarda `usn-state.json` (id del diario, siguiente USN,
118
146
 
119
147
  ## Rendimiento
120
148
 
149
+ Contenido medido en la misma máquina (caché caliente): 20 000 archivos / 40 MB → 15,1 s y 71 MB; 200 000 archivos / 331 MB → 31,5 s y 711 MB; refresco incremental del índice completo (1,3 M entradas) 2,9 s; reiniciar reutiliza la base y tarda 4,6 s en el corpus de 200 000 archivos. La base ocupa 2–3 veces el texto indexado.
150
+
121
151
  - 1,2 M de entradas: consulta en caliente 25–35 ms; búsqueda durante el escaneo ~2 ms.
122
152
  - `C:\Windows` (164 216 entradas) indexado por USN en 7,8 s en un runner de CI; consulta 9,6 ms.
123
153
  - `locator_export` de 1,24 M de filas: 477 MB, 6,4 s.
@@ -147,7 +177,7 @@ El motor Go vive en `engine/`; pruebas: `cd engine && go test ./...`.
147
177
 
148
178
  ## Límites
149
179
 
150
- * Solo nombres, rutas, tamaños y fechas: **no indexa contenido**. Para contenido usa `grep`.
180
+ * La búsqueda por contenido solo lee archivos de texto (por extensión) y los registros de sesión del DSH; los PDF, documentos de Office e imágenes no se extraen. De forma predeterminada el cuerpo indexado se limita a 1 GB / 200.000 archivos, así que en un directorio personal enorme solo queda la primera parte que se alcanza.
151
181
  * No es un driver de sistema de archivos; cubre solo las raíces configuradas.
152
182
  * inotify consume un descriptor por directorio; árboles enormes pueden agotar el límite del kernel (se avisa y la instantánea sigue usable).
153
183
  * Escrituras WSL en `/mnt/c` pueden no generar notificaciones de Windows; reconstruye esas raíces.
@@ -155,6 +185,8 @@ El motor Go vive en `engine/`; pruebas: `cd engine && go test ./...`.
155
185
 
156
186
  ## Seguridad y privacidad
157
187
 
188
+ La base de datos de contenido y el bloqueo de compilación se crean con permisos `0600`, y el directorio de estado con `0700`: otros usuarios locales no pueden leer los documentos indexados ni el texto de las sesiones.
189
+
158
190
  El índice es local: sin red, sin telemetría, sin subidas. La instantánea contiene nombres y rutas; trátala como datos privados. El plugin nunca modifica, mueve ni borra los archivos indexados.
159
191
 
160
192
  ## Licencia
package/README-hi.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **DeepSeek Harness के लिए पूरे डिस्क पर मिलीसेकंड में फ़ाइल खोज — Everything के बिना।**
6
6
 
7
- अपना क्रॉस-प्लेटफ़ॉर्म इंडेक्स (Windows USN/MFT · macOS FSEvents · Linux inotify), एजेंट के लिए चार टूल और क्रमबद्ध करने योग्य Web पैनल।
7
+ अपना क्रॉस-प्लेटफ़ॉर्म इंडेक्स (Windows USN/MFT · macOS FSEvents · Linux inotify), एजेंट के लिए छह टूल — नाम, सामग्री और सत्र — और क्रमबद्ध करने योग्य Web पैनल।
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
@@ -62,12 +62,12 @@ npm पैकेज में `linux-x64`, `linux-arm64`, `win32-x64`, `win32-ar
62
62
 
63
63
  ![dsh-locator खोज पैनल: पथ से फ़िल्टर किए गए Markdown परिणाम, हिट संख्या, समय और क्रमबद्ध कॉलम](docs/assets/panel-en.png)
64
64
 
65
- Web GUI में कहीं भी `Ctrl+Shift+F` दबाएँ, या साइडबार के निचले हिस्से में आवर्धक चिह्न पर क्लिक करें। पैनल उसी इंडेक्स को समान-मूल होस्ट रूट से क्वेरी करता है (`POST /dsh-locator/search`, `POST /dsh-locator/status`) — दूसरा सर्वर या अतिरिक्त पोर्ट नहीं।
65
+ Web GUI में कहीं भी `Ctrl+Shift+F` दबाएँ, या साइडबार के निचले हिस्से में आवर्धक चिह्न पर क्लिक करें। पैनल उसी इंडेक्स से होस्ट के इन रूटों के ज़रिए बात करता है — `/dsh-locator/search`, `/dsh-locator/content`, `/dsh-locator/status`, `/dsh-locator/reveal` और `/dsh-locator/open`। सब कुछ same-origin से चलता है — अलग सर्वर या अतिरिक्त पोर्ट की ज़रूरत नहीं।
66
66
 
67
67
  * खाली क्वेरी: तारीख़ के अनुसार हाल की प्रविष्टियाँ; टाइप करते ही 110 ms डिबाउंस और टूल जैसी ही सिंटैक्स।
68
68
  * कॉलम हेडर पर क्लिक करके नाम, आकार, तारीख़ या पथ से क्रम; दोबारा क्लिक पर उलटा।
69
- * `केवल फ़ाइलें` / `केवल फ़ोल्डर`; निचली पट्टी में हिट, सीमा और समय।
70
- * `↑`/`↓` चयन, `Enter` कॉपी, `Ctrl+O` खोलें, `Ctrl+Enter` कंपोज़र में `@"पथ"` डालें, डबल-क्लिक फ़ाइल मैनेजर में दिखाएँ, `Esc` बंद।
69
+ * `केवल फ़ाइलें` / `केवल फ़ोल्डर` टाइप फ़िल्टर करते हैं; **सामग्री खोजें** चेकबॉक्स टिक करने पर खोज नाम की जगह फ़ाइलों के अंदर के टेक्स्ट और DSH सेशन संदेशों में चलती है, और हर हिट के साथ एक-लाइन का स्निपेट दिखता है। निचली पट्टी में हिट, सीमा और समय।
70
+ * `↑`/`↓` चयन, `Enter` कॉपी, `Ctrl+O` खोलें, `Ctrl+Enter` कंपोज़र में `@"पथ"` डालें। किसी फ़ाइल पर डबल-क्लिक उसे फ़ाइल मैनेजर में दिखाता है; सेशन वाले परिणाम पर डबल-क्लिक उस बातचीत को खोलता है और मैच हुए संदेश पर कुछ देर के लिए आउटलाइन दिखाता है। `Esc` बंद।
71
71
  * इंटरफ़ेस `navigator.language` का पालन करता है (zh/en/es/pt/hi)।
72
72
 
73
73
  ## टूल
@@ -75,10 +75,24 @@ Web GUI में कहीं भी `Ctrl+Shift+F` दबाएँ, या स
75
75
  | टूल | काम |
76
76
  |---|---|
77
77
  | `locator_search` | Everything-शैली सिंटैक्स से इंडेक्स क्वेरी। |
78
+ | `locator_find` | फ़ाइलों के अंदर के टेक्स्ट और DSH सेशन संदेशों में खोज; हर हिट के साथ स्निपेट, और बातचीत वाले हिट के लिए `sessionId`/`messageId`। |
78
79
  | `locator_status` | रूट, प्रविष्टियाँ, watcher, USN मोड और त्रुटियाँ। |
79
80
  | `locator_rebuild` | पृष्ठभूमि में पुनर्निर्माण; खोज पुराने इंडेक्स से चलती रहती है। |
81
+ | `locator_reindex` | सामग्री इंडेक्स ताज़ा करें या दोबारा बनाएँ: डिफ़ॉल्ट में इंक्रीमेंटल, `full: true` से पूरा डेटाबेस हटाकर नया बनता है। |
80
82
  | `locator_export` | केवल-पढ़ने योग्य SQLite प्रति (`entries(path, name, dir, size, mtime_ms, ext)`)। |
81
83
 
84
+ ### सामग्री खोज
85
+
86
+ `locator_find` टेक्स्ट फ़ाइलों के अंदर के टेक्स्ट और DSH सेशन संदेशों को स्थानीय SQLite FTS5 इंडेक्स से खोजता है। चीनी और अंग्रेज़ी में सबस्ट्रिंग खोज चलती है — शब्द-विभाजन (segmentation) की ज़रूरत नहीं।
87
+
88
+ हर हिट के साथ एक स्निपेट मिलता है, और बातचीत वाले हिट के साथ `sessionId`/`messageId` भी — इसी से पैनल उस सेशन को खोलकर मैच हुए संदेश पर निशान लगा पाता है।
89
+
90
+ सामग्री इंडेक्स डिफ़ॉल्ट रूप से आलसी (lazy) है: प्लगइन शुरू होने पर कुछ नहीं बनता, पहली सामग्री खोज उसे बैकग्राउंड में शुरू करती है (`contentLazy: false` से शुरू से बनेगा)। प्लगइन दोबारा शुरू होने पर मौजूदा डेटाबेस का ही इस्तेमाल करता है और उसे इंक्रीमेंटल तरीके से रिफ़्रेश करता है; पूरा बिल्ड (पहले सेशन लॉग, फिर फ़ाइलों का अंदरूनी टेक्स्ट) सिर्फ़ तब चलता है जब डेटाबेस मौजूद न हो, उसका स्कीमा वर्ज़न पुराना हो, या वह खुल ही न पाए। उसके बाद हर `contentRefreshMs` (5 मिनट) पर इंक्रीमेंटल रिफ़्रेश होता है, जो सिर्फ़ उन्हीं फ़ाइलों और सेशनों को दोबारा पढ़ता है जिनका mtime बदला हो। `locator_reindex` से दोनों में से कोई भी मोड हाथ से चलाया जा सकता है (`full: true` पूरा डेटाबेस हटाकर फिर से बनाता है)।
91
+
92
+ जब तक बिल्ड चल रहा है, खोज उसी डेटा से जवाब देती रहती है जो अब तक इंडेक्स हो चुका है, और टूल तथा पैनल दोनों बिल्ड की स्थिति बताते हैं।
93
+
94
+ सीमाएँ: एक टेक्स्ट फ़ाइल से अधिकतम 1 MB, कुल 200,000 फ़ाइलें, सबसे हाल के 100 सेशन, और टेक्स्ट की कुल मात्रा 1 GB — ये सब कॉन्फ़िग में बदले जा सकते हैं। डिपेंडेंसी और टूलचेन फ़ोल्डर (`node_modules`, `.cargo`, `.bun`, `.pnpm`, `site-packages`, `toolchain`, …) डिफ़ॉल्ट रूप से छोड़े जाते हैं — वरना वे पहले पूरा बजट खा जाते और आपके अपने दस्तावेज़ नहीं इंडेक्स होते — `contentExcludes` से यह सूची बदल सकते हैं। इंडेक्स फ़ाइलें पढ़ता और zstd डिकोड करता है असिंक्रोनस रूप से, और हर ~8 ms पर event loop छोड़ता है (28.6 MB: 968 ms → 19 ms), इसलिए सत्र लोड/स्विच नहीं रुकते; आर्काइव किए सेशन डिफ़ॉल्ट रूप से नहीं इंडेक्स होते (`contentIncludeArchived: true` से होंगे), और जो सेशन अभी लिखा जा रहा है वह `contentActiveSessionDelayMs` तक रुकता है।
95
+
82
96
  ### क्वेरी सिंटैक्स
83
97
 
84
98
  ```text
@@ -104,8 +118,20 @@ case:1 wholeword:1 केस / पूरा शब्द
104
118
  | `refreshIntervalMs` | नेटिव watcher न होने पर समय-समय पर पुनर्निर्माण। |
105
119
  | `windowsUsn` | USN/MFT तेज़ रास्ता (Windows, व्यवस्थापक; डिफ़ॉल्ट `false`)। |
106
120
  | `scanWorkers` | समानांतर डायरेक्टरी रीडर; 0 = CPU। |
121
+ | `contentIndex` | फ़ाइल-बॉडी और सेशन संदेशों का इंडेक्स बनाना है या नहीं (डिफ़ॉल्ट `true`)। |
122
+ | `contentLazy` | कंटेंट इंडेक्स पहली सामग्री खोज पर बनाएँ, प्लगइन शुरू होते ही नहीं; `false` = शुरू से बैकग्राउंड में बनाएँ (डिफ़ॉल्ट `true`)। |
123
+ | `contentMaxFileBytes` | एक टेक्स्ट फ़ाइल से अधिकतम इतने बाइट पढ़े जाते हैं (डिफ़ॉल्ट `1000000`)। |
124
+ | `contentMaxTotalBytes` | कंटेंट डेटाबेस में रखे जाने वाले कुल बाइट (डिफ़ॉल्ट `1000000000`); `0` = कोई सीमा नहीं। |
125
+ | `contentMaxFiles` | एक बिल्ड में अधिकतम इतनी टेक्स्ट फ़ाइलें इंडेक्स होती हैं (डिफ़ॉल्ट `200000`); `0` = कोई सीमा नहीं। |
126
+ | `contentMaxSessions` | सबसे हाल के इतने सेशन लॉग इंडेक्स होते हैं (डिफ़ॉल्ट `100`); `0` = सभी। |
127
+ | `contentRefreshMs` | सामग्री के इंक्रीमेंटल रिफ़्रेश का अंतराल, मिलीसेकंड में (डिफ़ॉल्ट `300000`); `0` = अपने-आप रिफ़्रेश बंद। |
128
+ | `contentIncludeArchived` | आर्काइव किए गए सेशन भी इंडेक्स करें; डिफ़ॉल्ट `false`। |
129
+ | `contentActiveSessionDelayMs` | सेशन लॉग इतनी देर शांत रहने के बाद ही इंडेक्स होता है (डिफ़ॉल्ट `120000`)। |
130
+ | `contentSessionDirs` | अतिरिक्त सेशन डायरेक्टरियाँ (डिफ़ॉल्ट `[]`); खाली = `$DSH_HOME/sessions`। |
131
+ | `contentExcludes` | सामग्री इंडेक्स जिन पथ हिस्सों को छोड़ता है (डिपेंडेंसी/टूलचेन); नाम इंडेक्स पर असर नहीं। |
107
132
  | `stateDir` | स्नैपशॉट डायरेक्टरी; खाली = सिस्टम कैश। |
108
133
 
134
+
109
135
  ## प्लेटफ़ॉर्म
110
136
 
111
137
  | प्लेटफ़ॉर्म | स्कैन | अपडेट |
@@ -118,6 +144,8 @@ Windows पर `scan --usn` `usn-state.json` (जर्नल id, अगला U
118
144
 
119
145
  ## प्रदर्शन
120
146
 
147
+ उसी मशीन पर मापा गया (वार्म कैश): 20,000 फ़ाइलें / 40 MB → 15.1 s और 71 MB; 2,00,000 फ़ाइलें / 331 MB → 31.5 s और 711 MB; पूरे इंडेक्स (13 लाख प्रविष्टियाँ) का इंक्रीमेंटल रिफ़्रेश 2.9 s; रीस्टार्ट पर डेटाबेस दोबारा इस्तेमाल होता है और 2,00,000 फ़ाइलों वाले कॉर्पस में 4.6 s लगते हैं। डेटाबेस इंडेक्स किए टेक्स्ट का 2–3 गुना होता है।
148
+
121
149
  - 1.2 M प्रविष्टियाँ: गरम क्वेरी 25–35 ms; स्कैन के दौरान खोज ~2 ms।
122
150
  - `C:\Windows` (164,216 प्रविष्टियाँ) USN से CI runner पर 7.8 s में इंडेक्स; क्वेरी 9.6 ms।
123
151
  - 1.24 M पंक्तियों का `locator_export`: 477 MB, 6.4 s।
@@ -147,7 +175,7 @@ Go इंजन `engine/` में है; टेस्ट: `cd engine && go te
147
175
 
148
176
  ## सीमाएँ
149
177
 
150
- * केवल नाम, पथ, आकार और तारीख़: **सामग्री इंडेक्स नहीं**। सामग्री के लिए `grep`।
178
+ * सामग्री खोज सिर्फ़ टेक्स्ट फ़ाइलें (एक्सटेंशन से) और DSH सत्र लॉग पढ़ती है; PDF, Office दस्तावेज़ और तस्वीरों का टेक्स्ट नहीं निकाला जाता। डिफ़ॉल्ट रूप से इंडेक्स किया गया टेक्स्ट 1 GB / 2,00,000 फ़ाइलों तक सीमित है, इसलिए बहुत बड़े होम डायरेक्टरी में सिर्फ़ पहला हिस्सा ही रहता है।
151
179
  * यह फ़ाइल-सिस्टम ड्राइवर नहीं; केवल कॉन्फ़िगर किए रूट कवर करता है।
152
180
  * inotify प्रति डायरेक्टरी एक descriptor लेता है; बहुत बड़े पेड़ कर्नेल सीमा पार कर सकते हैं (चेतावनी मिलती है, स्नैपशॉट उपयोगी रहता है)।
153
181
  * WSL की `/mnt/c` लेख Windows सूचनाएँ नहीं दे सकतीं; उन रूट को फिर से बनाएँ।
@@ -155,6 +183,8 @@ Go इंजन `engine/` में है; टेस्ट: `cd engine && go te
155
183
 
156
184
  ## सुरक्षा और गोपनीयता
157
185
 
186
+ कंटेंट डेटाबेस और बिल्ड लॉक `0600` के साथ बनाए जाते हैं, और स्टेट डायरेक्टरी `0700` के साथ: स्थानीय दूसरे उपयोगकर्ता इंडेक्स किए गए दस्तावेज़ या सत्र टेक्स्ट नहीं पढ़ सकते।
187
+
158
188
  इंडेक्स स्थानीय है: कोई नेटवर्क, टेलीमेट्री या अपलोड नहीं। स्नैपशॉट में नाम और पथ हैं, इसे निजी डेटा मानें। प्लगइन इंडेक्स की गई फ़ाइलों को कभी बदलता, हटाता या स्थानांतरित नहीं करता।
159
189
 
160
190
  ## लाइसेंस
package/README-pt.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Busca de arquivos em todo o disco, em milissegundos, para o DeepSeek Harness — sem o Everything.**
6
6
 
7
- Índice próprio multiplataforma (Windows USN/MFT · macOS FSEvents · Linux inotify), quatro ferramentas para o agente e um painel web ordenável.
7
+ Índice próprio multiplataforma (Windows USN/MFT · macOS FSEvents · Linux inotify), seis ferramentas para o agente — nomes, conteúdo e sessões — e um painel web ordenável.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
@@ -62,12 +62,12 @@ Ao instalar num perfil Web já em execução, **recarregue a página** (`F5`): a
62
62
 
63
63
  ![Painel de busca do dsh-locator: resultados Markdown filtrados por caminho, com contagem, tempo e colunas ordenáveis](docs/assets/panel-en.png)
64
64
 
65
- Pressione `Ctrl+Shift+F` em qualquer lugar da Web GUI, ou clique na lupa no rodapé da barra lateral. O painel consulta o mesmo índice por rotas do host na mesma origem (`POST /dsh-locator/search`, `POST /dsh-locator/status`), sem segundo servidor e sem porta extra.
65
+ Pressione `Ctrl+Shift+F` em qualquer lugar da Web GUI, ou clique na lupa no rodapé da barra lateral. O painel se comunica com o host por rotas na mesma origem (`/dsh-locator/search`, `/dsh-locator/content`, `/dsh-locator/status`, `/dsh-locator/reveal` e `/dsh-locator/open`), sem segundo servidor e sem porta extra.
66
66
 
67
67
  * Consulta vazia: entradas recentes por data; ao digitar, debounce de 110 ms e a mesma sintaxe das ferramentas.
68
68
  * Clique no cabeçalho para ordenar por nome, tamanho, data ou caminho; outro clique inverte.
69
- * `Somente arquivos` / `Somente pastas`; o rodapé mostra resultados, limite e tempo.
70
- * `↑`/`↓` seleciona, `Enter` copia, `Ctrl+O` abre, `Ctrl+Enter` insere `@"caminho"` no compositor, clique duplo revela no gerenciador, `Esc` fecha.
69
+ * `Somente arquivos` / `Somente pastas` filtram o tipo; marcar **Buscar conteúdo** alterna entre a busca por nomes e a busca no conteúdo dos arquivos e das mensagens de sessão, e os acertos mostram um trecho de uma linha. O rodapé mostra resultados, limite e tempo.
70
+ * `↑`/`↓` seleciona, `Enter` copia, `Ctrl+O` abre, `Ctrl+Enter` insere `@"caminho"` no compositor; o clique duplo revela um arquivo no gerenciador de arquivos e, num resultado de sessão, abre a conversa correspondente e destaca a mensagem encontrada por um instante. `Esc` fecha.
71
71
  * A interface segue `navigator.language` (zh/en/es/pt/hi).
72
72
 
73
73
  ## Ferramentas
@@ -75,10 +75,22 @@ Pressione `Ctrl+Shift+F` em qualquer lugar da Web GUI, ou clique na lupa no roda
75
75
  | Ferramenta | Uso |
76
76
  |---|---|
77
77
  | `locator_search` | Consulta com sintaxe estilo Everything. |
78
+ | `locator_find` | Busca no corpo de arquivos e nas mensagens de sessão do DSH; os acertos trazem um trecho e, em conversas, `sessionId`/`messageId`. |
78
79
  | `locator_status` | Raízes, entradas, watcher, modo USN e erros. |
79
80
  | `locator_rebuild` | Reconstrói em segundo plano; buscas continuam no índice antigo. |
81
+ | `locator_reindex` | Atualiza ou reconstrói o índice de conteúdo: incremental por padrão, `full: true` descarta e reconstrói. |
80
82
  | `locator_export` | Cópia somente leitura em SQLite (`entries(path, name, dir, size, mtime_ms, ext)`). |
81
83
 
84
+ ### Busca por conteúdo
85
+
86
+ `locator_find` procura no corpo de arquivos de texto e nas mensagens de sessão do DSH por meio de um índice FTS5 local em SQLite. Subcadeias em chinês e inglês funcionam sem segmentação de palavras; cada acerto traz um trecho e, quando vem de uma conversa, `sessionId` e `messageId`.
87
+
88
+ O índice de conteúdo é preguiçoso por padrão: nada é construído na inicialização e a primeira busca de conteúdo inicia a construção em segundo plano (`contentLazy: false` constrói desde o início). Ao reiniciar, o plugin reaproveita o banco existente e o atualiza de forma incremental; a indexação completa (primeiro os logs de sessão, depois o corpo dos arquivos) só roda quando o banco não existe, tem uma versão de esquema antiga ou não pode ser aberto. Depois disso, uma atualização incremental a cada `contentRefreshMs` (5 minutos) relê apenas os arquivos e as sessões cujo mtime mudou. O `locator_reindex` dispara um dos dois modos manualmente.
89
+
90
+ Enquanto uma indexação está em andamento, as buscas continuam respondendo com o que já está indexado; a ferramenta e o painel mostram o estado da indexação.
91
+
92
+ Os limites padrão são 1 MB por arquivo de texto, 200.000 arquivos, as 100 sessões mais recentes e 1 GB de texto — tudo configurável. Pastas de dependências e toolchains (`node_modules`, `.cargo`, `.bun`, `.pnpm`, `site-packages`, `toolchain`, …) ficam de fora por padrão — senão consomem o orçamento antes dos seus próprios documentos — e `contentExcludes` muda essa lista. O índice lê e descomprime de forma assíncrona e cede o event loop a cada ~8 ms (28,6 MB: 968 ms → 19 ms), então não trava o carregamento nem a troca de sessão; sessões arquivadas ficam de fora a menos que `contentIncludeArchived: true`, e uma sessão que está sendo escrita espera `contentActiveSessionDelayMs`.
93
+
82
94
  ### Sintaxe de consulta
83
95
 
84
96
  ```text
@@ -104,8 +116,20 @@ case:1 wholeword:1 maiúsculas / palavra inteira
104
116
  | `refreshIntervalMs` | Reconstrução periódica quando não há watcher nativo. |
105
117
  | `windowsUsn` | Caminho rápido USN/MFT (Windows, administrador; padrão `false`). |
106
118
  | `scanWorkers` | Leitores de diretório em paralelo; 0 = CPU. |
119
+ | `contentIndex` | Constrói o índice de conteúdo de arquivos e sessões (padrão `true`). |
120
+ | `contentLazy` | Constrói o índice de conteúdo na primeira busca de conteúdo em vez de na inicialização; `false` constrói em segundo plano desde o início (padrão `true`). |
121
+ | `contentMaxFileBytes` | Bytes lidos de cada arquivo de texto (padrão `1000000`). |
122
+ | `contentMaxTotalBytes` | Total de bytes de conteúdo mantido; `0` = sem limite (padrão `1000000000`). |
123
+ | `contentMaxFiles` | Máximo de arquivos de texto indexados a cada execução; `0` = sem limite (padrão `200000`). |
124
+ | `contentMaxSessions` | Sessões mais recentes indexadas; `0` = todas (padrão `100`). |
125
+ | `contentRefreshMs` | Ritmo da atualização incremental de conteúdo; `0` desativa (padrão `300000`). |
126
+ | `contentIncludeArchived` | Indexa também sessões arquivadas; padrão `false`. |
127
+ | `contentActiveSessionDelayMs` | Só indexa um log de sessão depois de ficar quieto esse tempo (padrão `120000`). |
128
+ | `contentSessionDirs` | Diretórios adicionais de sessões; vazio = `$DSH_HOME/sessions`. |
129
+ | `contentExcludes` | Trechos de caminho que a busca por conteúdo ignora (dependências e toolchains); o índice de nomes continua igual. |
107
130
  | `stateDir` | Diretório do instantâneo; vazio = cache do sistema. |
108
131
 
132
+
109
133
  ## Plataformas
110
134
 
111
135
  | Plataforma | Varredura | Atualizações |
@@ -118,6 +142,8 @@ No Windows, `scan --usn` grava `usn-state.json` (id do diário, próximo USN, ra
118
142
 
119
143
  ## Desempenho
120
144
 
145
+ Conteúdo medido na mesma máquina (cache quente): 20.000 arquivos / 40 MB → 15,1 s e 71 MB; 200.000 arquivos / 331 MB → 31,5 s e 711 MB; atualização incremental do índice completo (1,3 M entradas) 2,9 s; reiniciar reaproveita o banco e leva 4,6 s no corpus de 200.000 arquivos. O banco ocupa 2–3 vezes o texto indexado.
146
+
121
147
  - 1,2 M de entradas: consulta a quente 25–35 ms; busca durante a varredura ~2 ms.
122
148
  - `C:\Windows` (164.216 entradas) indexado por USN em 7,8 s num runner de CI; consulta 9,6 ms.
123
149
  - `locator_export` de 1,24 M de linhas: 477 MB, 6,4 s.
@@ -147,7 +173,7 @@ O motor Go fica em `engine/`; testes: `cd engine && go test ./...`.
147
173
 
148
174
  ## Limites
149
175
 
150
- * Só nomes, caminhos, tamanhos e datas: **não indexa conteúdo**. Para conteúdo use `grep`.
176
+ * A busca por conteúdo lê apenas arquivos de texto (por extensão) e os logs de sessão do DSH; PDFs, documentos do Office e imagens não têm o texto extraído. Por padrão o corpo indexado fica limitado a 1 GB / 200.000 arquivos, então um diretório pessoal enorme guarda só a primeira fatia alcançada.
151
177
  * Não é um driver de sistema de arquivos; cobre apenas as raízes configuradas.
152
178
  * inotify usa um descritor por diretório; árvores enormes podem estourar o limite do kernel (há aviso e o instantâneo continua utilizável).
153
179
  * Escritas do WSL em `/mnt/c` podem não gerar notificações do Windows; reconstrua essas raízes.
@@ -155,6 +181,8 @@ O motor Go fica em `engine/`; testes: `cd engine && go test ./...`.
155
181
 
156
182
  ## Segurança e privacidade
157
183
 
184
+ O banco de dados de conteúdo e o bloqueio de compilação são criados com permissões `0600`, e o diretório de estado com `0700`: outros usuários locais não podem ler os documentos indexados nem o texto das sessões.
185
+
158
186
  O índice é local: sem rede, sem telemetria, sem uploads. O instantâneo contém nomes e caminhos; trate-o como dado privado. O plugin nunca modifica, move ou apaga os arquivos indexados.
159
187
 
160
188
  ## Licença
package/README.en.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Whole-disk, millisecond file search for DeepSeek Harness — without Everything.**
6
6
 
7
- A self-built cross-platform index (Windows USN/MFT · macOS FSEvents · Linux inotify), four agent tools and a sortable Web panel.
7
+ A self-built cross-platform index (Windows USN/MFT · macOS FSEvents · Linux inotify), six agent tools — names, contents and sessions — and a sortable Web panel.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/dsh-locator)](https://www.npmjs.com/package/dsh-locator)
@@ -83,7 +83,8 @@ Press `Ctrl+Shift+F` anywhere in the Web GUI, or click the magnifier at the side
83
83
  * Empty query lists the most recently modified entries; typing runs the same Everything-style syntax as the tool after a 110 ms debounce.
84
84
  * Click a column header to sort by name, size, modification time or path; toggle ascending/descending.
85
85
  * `只看文件` / `只看目录` filter the kind; the footer reports matches, the page cap and query latency.
86
- * `↑`/`↓` select, `Enter` copies the selected path, `Ctrl+O` opens it, `Ctrl+Enter` inserts a quoted `@"path"` into the composer, **double-click reveals it** in Explorer/Finder/`xdg-open`, `Esc` closes.
86
+ * Tick **Search content** (`搜内容`) to switch from names to file bodies and session messages; hits show a one-line snippet, and a session hit carries its conversation.
87
+ * `↑`/`↓` select, `Enter` copies the selected path, `Ctrl+O` opens it, `Ctrl+Enter` inserts a quoted `@"path"` into the composer, **double-click reveals a file** in Explorer/Finder/`xdg-open`, and **double-click opens a session hit** at the matched message, outlined for a moment, `Esc` closes.
87
88
  * The panel follows `navigator.language` (zh/en/es/pt/hi).
88
89
  * The panel is optional: profiles without a Web server never mount it, and the three tools keep working.
89
90
 
@@ -92,13 +93,15 @@ Press `Ctrl+Shift+F` anywhere in the Web GUI, or click the magnifier at the side
92
93
  | Tool | What it does |
93
94
  |---|---|
94
95
  | `locator_search` | Query the index. Returns path, size, modification time, extension and kind, plus total/truncated so a capped page is never presented as complete. |
96
+ | `locator_find` | Search file bodies and DSH session messages. Hits return a snippet plus `sessionId`/`messageId` for conversations. |
95
97
  | `locator_status` | Roots, entry counts, watcher state, state directory and engine path. Use it to explain coverage or diagnose a stale result. |
96
98
  | `locator_rebuild` | Replace the index with a fresh background scan. Searches keep using the old index until the new one is ready. |
99
+ | `locator_reindex` | Refresh or rebuild the content index: incremental by default, `full: true` drops and rebuilds it. |
97
100
  | `locator_export` | Write a read-only SQLite mirror of the index (`entries` table) for external SQL tools. Streams page by page; the binary snapshot stays primary. |
98
101
 
99
102
  ### Content search
100
103
 
101
- `locator_find` searches the **bodies** of text files and DSH session messages through a local SQLite FTS5 index. Hits return a snippet plus `sessionId`/`messageId` for conversations, so the panel can open the session and scroll to the matched text. The index is built in the background after the name scan; text files are capped by `contentMaxFileBytes`, the total body store by `contentMaxTotalBytes`, and the most recent `contentMaxSessions` session logs are indexed by default. An incremental refresh runs every `contentRefreshMs` (2 minutes) and only re-reads changed files/sessions; `locator_reindex` triggers it manually (`full: true` rebuilds everything).
104
+ `locator_find` searches the **bodies** of text files and DSH session messages through a local SQLite FTS5 index. Hits return a snippet plus `sessionId`/`messageId` for conversations, so the panel can open the session and outline the matched message. The content index is **lazy by default**: nothing is built at plugin start, and the first body/session search (`locator_find` or the panel's content mode) starts the build in the background — `contentLazy: false` restores the eager build. A restart with an existing database refreshes it incrementally on first use; a full build (session logs first, then file bodies) only runs when the database is missing, carries a stale schema version or cannot be opened. An incremental refresh then follows every `contentRefreshMs` (5 minutes), re-reading only files and sessions whose mtime changed; `locator_reindex` triggers either mode manually (`full: true` drops and rebuilds the database). While a build runs, searches keep answering from what is already indexed and `state.building` reports progress. Text files are capped by `contentMaxFileBytes`, the total body store by `contentMaxTotalBytes`, and only the most recent `contentMaxSessions` session logs are indexed. Dependency and toolchain trees (`node_modules`, `.cargo`, `.bun`, `.pnpm`, `site-packages`, `toolchain`, …) are skipped by default — otherwise they exhaust the byte budget before your own documents — and `contentExcludes` changes that list. The index runs inside the DSH host process, but file reads and zstd decoding are asynchronous and both build loops yield about every 8 ms: on a 28.6 MB corpus the longest event-loop stall dropped from 968 ms to 19 ms, so session loading and switching stay responsive. Content queries themselves run in a worker thread with a read-only connection, so a slow search cannot freeze the host while you type. Sessions deleted from disk leave the index on the next refresh; archived sessions are skipped unless `contentIncludeArchived: true`; a session log that is still being written is deferred until it has been quiet for `contentActiveSessionDelayMs`, instead of being fully decompressed on every refresh.
102
105
 
103
106
  ### Query language
104
107
 
@@ -133,6 +136,17 @@ Every field is a Schemastery config field; edit the `dsh-locator` entry in the p
133
136
  | `refreshIntervalMs` | `1800000` | Rebuild cadence when no native watcher is active (kqueue fallback or a watcher failure); `0` disables. |
134
137
  | `scanWorkers` | `0` | Directory reader concurrency during scans; `0` uses the CPU count. |
135
138
  | `memoryLimitMB` | `0` | Soft Go heap limit for the engine in MiB; `0` leaves it to `GOMEMLIMIT`/`GOGC`. |
139
+ | `contentIndex` | `true` | Build the file-body and session-message index at all. |
140
+ | `contentLazy` | `true` | Build the content index on the first content search instead of at plugin start; `false` builds it eagerly in the background. |
141
+ | `contentMaxFileBytes` | `1000000` | Bytes read from one text file. |
142
+ | `contentMaxTotalBytes` | `1000000000` | Total body bytes kept in the content database; `0` means unlimited. |
143
+ | `contentMaxFiles` | `200000` | Text files indexed per build; `0` means unlimited. |
144
+ | `contentMaxSessions` | `100` | Most recent session logs indexed; `0` means all. |
145
+ | `contentRefreshMs` | `300000` | Incremental content refresh cadence; `0` disables it. |
146
+ | `contentIncludeArchived` | `false` | Index archived sessions as well; off by default. |
147
+ | `contentActiveSessionDelayMs` | `120000` | Only index a session log after it has been quiet this long. |
148
+ | `contentSessionDirs` | `[]` | Extra session directories; empty means `$DSH_HOME/sessions`. |
149
+ | `contentExcludes` | dependency/toolchain names | Path segments or substrings the content index skips; the name index still covers them. |
136
150
  | `windowsUsn` | `false` | Try the NTFS USN/MFT enumeration before the walk (Windows, administrator; falls back automatically). |
137
151
  | `stateDir` | `''` | Snapshot directory; empty means the platform cache directory. |
138
152
  | `binaryPath` | `''` | Override the bundled engine (development). |
@@ -191,6 +205,20 @@ Measured on a developer WSL2 machine with a Linux home directory (1,234,757 entr
191
205
 
192
206
  Search is parallel over the record array; the slower cold scan is disk-bound. Numbers vary with the filesystem, cache state and CPU.
193
207
 
208
+ ### Content search
209
+
210
+ Measured on the same machine, warm page cache:
211
+
212
+ | Corpus | Full build | `content.db` | Incremental (no change) | Query p50 / p95 |
213
+ |---|---|---|---|---|
214
+ | 20k files / 40 MB of bodies | 15.1 s | 71 MB | 0.17 s | 5 / 17 ms |
215
+ | 200k files / 331 MB of bodies | 31.5 s | 711 MB | 1.3 s | 5 / 156 ms |
216
+ | Real home, 177k documents at the 2 GB cap | 7.4 min | 5.99 GB | 2.9 s | — |
217
+
218
+ * The database is roughly 2–3× the indexed body bytes; leave disk headroom. Dependency and toolchain trees (`contentExcludes`) are skipped by default — otherwise they eat the byte budget before your own documents.
219
+ * A restart reuses `content.db` and refreshes incrementally (63.4 s → 4.6 s on the 200k corpus). A full rebuild only runs on the first start after an upgrade, a stale schema or an unreadable database.
220
+ * When `contentMaxTotalBytes` is reached, `locator_find` says “content budget reached”; raise the cap or narrow `contentExcludes`.
221
+
194
222
  ## Design
195
223
 
196
224
  ![dsh-locator architecture: filesystems → scan/watch → compact snapshot → query engine → agent tools / Web panel](docs/assets/architecture.svg)
@@ -233,7 +261,7 @@ The engine is a separate Go module under `engine/`; run its tests with `cd engin
233
261
 
234
262
  ## Limitations
235
263
 
236
- * File names, paths, sizes and modification times only — **no content indexing**. Use `grep` for contents.
264
+ * Content search reads text files (by extension) and DSH session logs only; PDFs, Office documents and images are not text-extracted. The body store is capped at 2 GB / 200,000 files by default, so a huge home directory keeps only the first slice it reaches.
237
265
  * The index covers configured roots; it is not a filesystem driver.
238
266
  * Linux inotify watches one descriptor per directory; a home directory with hundreds of thousands of directories can exceed the kernel limit. The plugin reports the warning and the snapshot stays usable; rebuilds catch up.
239
267
  * WSL writes into `/mnt/c` may not produce Windows notifications (the Windows watcher cannot see the 9p writer's change stream). Rebuild those roots explicitly.
@@ -255,6 +283,8 @@ The engine is a single Go process and honours the standard `GOMEMLIMIT` and `GOG
255
283
 
256
284
  ## Security and privacy
257
285
 
286
+ The content database and build lock are created `0600` and the state directory `0700`, so other local users cannot read indexed documents or session text.
287
+
258
288
  The index is local: no network calls, no telemetry, no uploads. The snapshot contains file names and paths, so treat the state directory as private data and exclude it from backups if that matters to you. The plugin never modifies, moves or deletes indexed files.
259
289
 
260
290
  ## License
package/README.md CHANGED
@@ -91,10 +91,14 @@ Everything 风格的查询语法,返回路径、大小、修改时间、扩展
91
91
  - 文件命中带路径,直接打开或定位;
92
92
  - 会话命中带 `sessionId` 和 `messageId`,可以跳回那次对话;
93
93
  - 中文和英文子串都能搜,不要求分词。
94
- - 启动后每 2 分钟做一次增量刷新,只重读 mtime 变化的文件/会话;也可以手动触发全量重建。
94
+ - 内容索引默认**惰性启动**:插件启动时什么都不建,第一次搜内容(`locator_find` 或面板“搜内容”)才开始在后台构建,`contentLazy: false` 可恢复启动即建。重启后复用已有的内容库、先做一次增量刷新;只有库不存在、结构版本不兼容或损坏时才全量重建(先会话、后文件正文)。之后每 5 分钟再增量刷新一次,只重读 mtime 变化的文件/会话;也可以手动触发全量重建。
95
95
 
96
96
  内容索引在后台构建,刚启动时可能只覆盖了一部分,工具和面板都会显示构建状态。默认只索引 4MB 以内的文本文件,最多 20 万个文件、最近 200 个会话,内容总量上限 2GB;这些都能在配置里改。
97
97
 
98
+ 依赖和工具链目录(`node_modules`、`.cargo`、`.bun`、`.pnpm`、`site-packages`、`toolchain` 等)默认不读正文,否则内容预算会先被库代码吃满、你自己的文档反而搜不到;要调整就改 `contentExcludes`。
99
+
100
+ 内容索引全程在宿主进程里跑,但读文件和 zstd 解压都是异步的,并且每约 8ms 就让出事件循环——实测 28.6MB 语料一次构建的最大事件循环阻塞从 968ms 降到 19ms,所以不会拖慢会话加载/切换。内容查询本身跑在 worker 线程里(只读连接),输入搜索时也不会卡住宿主。已删除的会话会随文件消失自动退出索引;已归档的会话默认不索引(`contentIncludeArchived: true` 才索引);正在写入的会话会等它安静 `contentActiveSessionDelayMs` 后再索引,不会每轮刷新把整份日志重新解压一遍。
101
+
98
102
  ## Web 面板
99
103
 
100
104
  ![搜索面板:勾选“搜内容”后在文件正文中搜索,显示命中片段和路径](docs/assets/panel-zh.png)
@@ -149,12 +153,16 @@ case:1 wholeword:1 区分大小写 / 全词匹配
149
153
  | `windowsUsn` | `false` | Windows 上尝试 USN/MFT 快路径,需要管理员权限。 |
150
154
  | `memoryLimitMB` | 0 | 引擎的 Go 软堆上限;0 表示交给 `GOMEMLIMIT`/`GOGC`。 |
151
155
  | `contentIndex` | `true` | 是否构建文件正文/会话内容索引。 |
152
- | `contentMaxFileBytes` | 4000000 | 单个文本文件最多读多少字节。 |
153
- | `contentMaxTotalBytes` | 2000000000 | 内容库总量上限;0 表示不限制。 |
156
+ | `contentLazy` | `true` | 惰性构建:第一次搜内容时才建库;`false` 表示插件启动后就在后台建。 |
157
+ | `contentMaxFileBytes` | 1000000 | 单个文本文件最多读多少字节。 |
158
+ | `contentMaxTotalBytes` | 1000000000 | 内容库总量上限;0 表示不限制。 |
154
159
  | `contentMaxFiles` | 200000 | 最多索引多少个文件;0 表示不限制。 |
155
- | `contentMaxSessions` | 200 | 最多索引最近多少个会话;0 表示全部。 |
156
- | `contentRefreshMs` | 120000 | 内容增量刷新周期;0 关闭自动刷新。 |
160
+ | `contentMaxSessions` | 100 | 最多索引最近多少个会话;0 表示全部。 |
161
+ | `contentRefreshMs` | 300000 | 内容增量刷新周期;0 关闭自动刷新。 |
162
+ | `contentIncludeArchived` | `false` | 是否索引已归档的会话;默认不索引。 |
163
+ | `contentActiveSessionDelayMs` | 120000 | 会话日志安静多久后才索引;正在写的会话不重复解析。 |
157
164
  | `contentSessionDirs` | `$DSH_HOME/sessions` | 额外的会话目录。 |
165
+ | `contentExcludes` | 依赖/工具链目录 | 内容索引跳过的路径片段或子串;文件名索引不受影响。 |
158
166
  | `stateDir` | 系统缓存 | 快照和内容数据库的位置。 |
159
167
 
160
168
  ## 平台支持
@@ -174,6 +182,18 @@ Windows 的 USN 快路径默认关闭(`windowsUsn: true`)。它需要管理
174
182
  - 导出 124 万行 SQLite:约 477MB,6.4 秒。
175
183
  - 内容索引是后台任务,首次构建时间取决于文本文件数量和会话日志大小。
176
184
 
185
+ 内容索引实测(同一台 WSL2 开发机,热缓存):
186
+
187
+ | 语料 | 全量构建 | `content.db` | 增量刷新(无变化) | 查询 p50 / p95 |
188
+ |---|---|---|---|---|
189
+ | 2 万文件 / 40MB 正文 | 15.1 s | 71 MB | 0.17 s | 5 / 17 ms |
190
+ | 20 万文件 / 331MB 正文 | 31.5 s | 711 MB | 1.3 s | 5 / 156 ms |
191
+ | 真实主目录(17.7 万文档 / 2GB 上限) | 7.4 min | 5.99 GB | 2.9 s | — |
192
+
193
+ - 内容库体积大约是正文的 2–3 倍,磁盘要留余量;默认跳过依赖/工具链目录(`contentExcludes`),否则 2GB 预算会被库代码先吃光。
194
+ - 重启会复用已有的内容库做增量刷新(20 万文件语料实测 63.4 s → 4.6 s);只有升级后首启、库损坏或结构版本不匹配时才全量重建。
195
+ - 撞到 `contentMaxTotalBytes` 时工具会明确提示 “content budget reached”,可以调大上限或收窄 `contentExcludes`。
196
+
177
197
  ## 设计
178
198
 
179
199
  ![架构:文件系统 → 扫描/监听 → 紧凑快照 → 查询引擎 → agent 工具 / Web 面板](docs/assets/architecture.svg)
@@ -209,6 +229,8 @@ Go 引擎在 `engine/` 下,测试用 `cd engine && go test ./...`。macOS 的
209
229
 
210
230
  ## 安全与隐私
211
231
 
232
+ 内容索引库与构建锁以 `0600` 创建、状态目录以 `0700` 创建,同机其他用户无法读取被索引的文档或会话正文。
233
+
212
234
  索引完全在本地,不联网、不上传、不埋点。快照和内容数据库里有文件名、路径和正文,请把它们当作私有数据。插件不会修改、移动或删除被索引的文件。
213
235
 
214
236
  ## 许可证