dsh-locator 0.8.2 → 0.10.0

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 (58) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README-es.md +30 -5
  3. package/README-hi.md +31 -5
  4. package/README-pt.md +31 -5
  5. package/README.en.md +31 -5
  6. package/README.md +31 -5
  7. package/bin/darwin-arm64/indexd +0 -0
  8. package/bin/darwin-x64/indexd +0 -0
  9. package/bin/linux-arm64/indexd +0 -0
  10. package/bin/linux-x64/indexd +0 -0
  11. package/bin/win32-arm64/indexd.exe +0 -0
  12. package/bin/win32-x64/indexd.exe +0 -0
  13. package/client.js +1198 -117
  14. package/lib/combined.d.ts +20 -3
  15. package/lib/combined.d.ts.map +1 -1
  16. package/lib/combined.js +85 -15
  17. package/lib/combined.js.map +1 -1
  18. package/lib/content-search-worker.d.ts +8 -3
  19. package/lib/content-search-worker.d.ts.map +1 -1
  20. package/lib/content-search-worker.js +54 -27
  21. package/lib/content-search-worker.js.map +1 -1
  22. package/lib/content-search.d.ts +127 -7
  23. package/lib/content-search.d.ts.map +1 -1
  24. package/lib/content-search.js +571 -36
  25. package/lib/content-search.js.map +1 -1
  26. package/lib/content.d.ts +83 -5
  27. package/lib/content.d.ts.map +1 -1
  28. package/lib/content.js +351 -87
  29. package/lib/content.js.map +1 -1
  30. package/lib/export.d.ts +61 -0
  31. package/lib/export.d.ts.map +1 -1
  32. package/lib/export.js +217 -52
  33. package/lib/export.js.map +1 -1
  34. package/lib/find.d.ts.map +1 -1
  35. package/lib/find.js +73 -16
  36. package/lib/find.js.map +1 -1
  37. package/lib/http.d.ts +38 -2
  38. package/lib/http.d.ts.map +1 -1
  39. package/lib/http.js +279 -82
  40. package/lib/http.js.map +1 -1
  41. package/lib/reveal.d.ts +69 -3
  42. package/lib/reveal.d.ts.map +1 -1
  43. package/lib/reveal.js +140 -11
  44. package/lib/reveal.js.map +1 -1
  45. package/lib/tools.d.ts +30 -0
  46. package/lib/tools.d.ts.map +1 -1
  47. package/lib/tools.js +65 -3
  48. package/lib/tools.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/combined.ts +107 -17
  51. package/src/content-search-worker.ts +52 -24
  52. package/src/content-search.ts +710 -36
  53. package/src/content.ts +349 -73
  54. package/src/export.ts +240 -50
  55. package/src/find.ts +83 -16
  56. package/src/http.ts +271 -83
  57. package/src/reveal.ts +148 -10
  58. package/src/tools.ts +83 -6
package/src/tools.ts CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { defineTool } from '@deepseek-ai/dsh-tools'
7
7
  import type { LocatorRuntime, SearchHit } from './runtime.ts'
8
+ import { resolveWithinRoots } from './reveal.ts'
8
9
 
9
10
  const HIT_SCHEMA = {
10
11
  type: 'object',
@@ -69,6 +70,77 @@ const REBUILD_OUTPUT = {
69
70
  },
70
71
  } as const
71
72
 
73
+ /** Validated rebuild scope: only narrowing of the configured roots/excludes is allowed. */
74
+ export interface RebuildScope {
75
+ roots?: string[]
76
+ excludes?: string[]
77
+ }
78
+
79
+ /** Validation result for one `locator_rebuild` argument pair. */
80
+ export type RebuildScopeResult =
81
+ | { ok: true; roots?: string[]; excludes?: string[] }
82
+ | { ok: false; error: string }
83
+
84
+ /**
85
+ * Restrict a `locator_rebuild` request to the configured scope.
86
+ *
87
+ * Root replacement is the dangerous knob: letting the model pick an arbitrary
88
+ * absolute path would turn a search tool into a disk-indexer for directories
89
+ * the operator never configured. A requested root must therefore canonicalize
90
+ * (symlinks resolved) to a configured root or a directory below one; requested
91
+ * excludes must be draw from the configured exclude list. Everything is passed
92
+ * through the same canonicalization as `/reveal`/`/open`.
93
+ * @param roots - requested replacement roots, if any.
94
+ * @param excludes - requested replacement excludes, if any.
95
+ * @param configuredRoots - the plugin's configured (resolved) roots.
96
+ * @param configuredExcludes - the plugin's configured excludes.
97
+ * @returns the canonical narrowed scope, or the first refusal.
98
+ */
99
+ export function validateRebuildArguments(
100
+ roots: readonly string[] | undefined,
101
+ excludes: readonly string[] | undefined,
102
+ configuredRoots: readonly string[],
103
+ configuredExcludes: readonly string[],
104
+ ): RebuildScopeResult {
105
+ let narrowedRoots: string[] | undefined
106
+ if (roots !== undefined) {
107
+ if (!Array.isArray(roots) || roots.length === 0) {
108
+ return { ok: false, error: 'roots must be a non-empty array of directory paths' }
109
+ }
110
+ narrowedRoots = []
111
+ for (const root of roots) {
112
+ if (typeof root !== 'string' || root.trim() === '') {
113
+ return { ok: false, error: 'roots must contain non-empty directory paths' }
114
+ }
115
+ const resolved = resolveWithinRoots(root.trim(), configuredRoots)
116
+ if (resolved === undefined) {
117
+ return { ok: false, error: `root is outside the configured roots: ${root}` }
118
+ }
119
+ if (!narrowedRoots.includes(resolved.path)) narrowedRoots.push(resolved.path)
120
+ }
121
+ }
122
+ let narrowedExcludes: string[] | undefined
123
+ if (excludes !== undefined) {
124
+ if (!Array.isArray(excludes)) return { ok: false, error: 'excludes must be an array of configured globs' }
125
+ const allowed = new Set(configuredExcludes)
126
+ narrowedExcludes = []
127
+ for (const exclude of excludes) {
128
+ if (typeof exclude !== 'string' || exclude.trim() === '') {
129
+ return { ok: false, error: 'excludes must contain non-empty globs' }
130
+ }
131
+ if (!allowed.has(exclude)) {
132
+ return { ok: false, error: `exclude is not one of the configured excludes: ${exclude}` }
133
+ }
134
+ if (!narrowedExcludes.includes(exclude)) narrowedExcludes.push(exclude)
135
+ }
136
+ }
137
+ return {
138
+ ok: true,
139
+ ...(narrowedRoots === undefined ? {} : { roots: narrowedRoots }),
140
+ ...(narrowedExcludes === undefined ? {} : { excludes: narrowedExcludes }),
141
+ }
142
+ }
143
+
72
144
  const SEARCH_DESCRIPTION = [
73
145
  'Search a persistent local file-name index in milliseconds (dsh-locator).',
74
146
  '',
@@ -264,11 +336,12 @@ export function makeRebuildTool(rt: LocatorRuntime) {
264
336
  name: 'locator_rebuild',
265
337
  description: [
266
338
  'Rebuild the local file index from scratch (dsh-locator). Runs in the background; locator_search keeps answering from the old index until the new one is ready.',
339
+ 'The rebuild is always confined to the configured scope: replacement `roots` must resolve inside the roots the plugin was configured with, and replacement `excludes` must come from the configured exclude list. To index a different directory, change the plugin configuration instead.',
267
340
  'Use it after changing indexed roots, after the index looks stale and the watcher is inactive, or when the user explicitly asks to rescan. It can take minutes for a large home directory and costs disk I/O, so do not call it routinely.',
268
341
  ].join('\n'),
269
342
  parameters: {
270
- roots: { type: 'array', items: { type: 'string' }, description: 'Replacement root directories (defaults to the current roots).' },
271
- excludes: { type: 'array', items: { type: 'string' }, description: 'Replacement exclusion globs.' },
343
+ roots: { type: 'array', items: { type: 'string' }, description: 'Replacement root directories (defaults to the current roots). Each must be a configured root or inside one.' },
344
+ excludes: { type: 'array', items: { type: 'string' }, description: 'Replacement exclusion globs (defaults to the current excludes). Each must already be configured.' },
272
345
  },
273
346
  output: {
274
347
  schema: REBUILD_OUTPUT,
@@ -279,12 +352,16 @@ export function makeRebuildTool(rt: LocatorRuntime) {
279
352
  },
280
353
  },
281
354
  async execute(args) {
355
+ const scope = validateRebuildArguments(
356
+ args.roots,
357
+ args.excludes,
358
+ rt.roots,
359
+ rt.config.excludes,
360
+ )
361
+ if (!scope.ok) return { ok: false as const, error: scope.error }
282
362
  try {
283
363
  rt.start()
284
- const result = await rt.rebuild(
285
- args.roots === undefined ? undefined : args.roots,
286
- args.excludes === undefined ? undefined : args.excludes,
287
- )
364
+ const result = await rt.rebuild(scope.roots, scope.excludes)
288
365
  return { ok: true as const, started: result.started }
289
366
  } catch (error) {
290
367
  return { ok: false as const, error: messageOf(error) }