@fishdivinity/dsh-local-search 0.2.0 → 0.3.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.
package/lib/tools.js CHANGED
@@ -7,10 +7,9 @@ import { parseRef, assertRefEngine } from './ref.js'
7
7
  import { defaultAllRoots, parseAllRoots, rootsFor } from './roots.js'
8
8
  import { readConfig } from './config.js'
9
9
  import { probeEngines } from './probe.js'
10
+ import { MAX_LIMIT, MAX_READ } from './limits.js'
10
11
 
11
12
  const STATUS_ENGINES = ['auto', ...new Set([...engineNames().files, ...engineNames().content])]
12
- const MAX_LIMIT = 100
13
- const MAX_READ = 20000
14
13
  const MAX_FRAGMENTS = 20
15
14
 
16
15
  function toHarnessError(error) {
@@ -30,13 +29,12 @@ async function guard(fn) {
30
29
  }
31
30
 
32
31
  /**
33
- * Coerce an optional numeric argument into `[1, cap]`. A non-numeric argument
34
- * falls back to the configured default instead of turning the window into NaN,
35
- * which would silently return nothing.
36
- * @param value - the argument as the model sent it
37
- * @param fallback - default to use when it is not a finite number
38
- * @param cap - inclusive upper bound
39
- * @returns the usable count
32
+ * 把可选的数字参数收进 `[1, cap]`。非数字参数回落到配置的默认值,而不是让窗口变成 NaN
33
+ * ——那样会静默返回空结果。
34
+ * @param value - 模型发来的参数原样
35
+ * @param fallback - 不是有限数字时用的默认值
36
+ * @param cap - 闭区间上界
37
+ * @returns 可用的条数
40
38
  */
41
39
  function clampCount(value, fallback, cap) {
42
40
  const number = Number.isFinite(value) ? Math.floor(value) : fallback
@@ -65,6 +63,19 @@ function validateEngine(engine, allowed) {
65
63
  return value
66
64
  }
67
65
 
66
+ /**
67
+ * 这次调用该用哪个后端。
68
+ *
69
+ * `auto`(或没传)跟随配置里选的后端,而不是「注册表里的第一个」:配置里的 backend 就是
70
+ * 「默认用哪个」的声明,自动选第一个会让那个字段变成死配置。
71
+ * @param {string|undefined} requested - 调用参数里的 engine
72
+ * @param {string} configured - 配置里选的后端
73
+ * @returns {string} 要解析的后端名
74
+ */
75
+ function engineFor(requested, configured) {
76
+ return requested === undefined || requested === '' || requested === 'auto' ? configured : requested
77
+ }
78
+
68
79
  function presentSearchCall(label) {
69
80
  return (args) => ({
70
81
  card: 'generic',
@@ -189,20 +200,19 @@ const statusOutput = {
189
200
  }
190
201
 
191
202
  /**
192
- * Build and register the five model-facing tools.
203
+ * 构建并注册五个面向模型的工具。
193
204
  * @param {import('@deepseek-ai/cordis').Context} ctx
194
205
  * @param {object} config
195
- * @returns {Array<() => void>} tool disposers
206
+ * @returns {Array<() => void>} 工具的 disposer
196
207
  */
197
208
  export function createTools(ctx, config) {
198
- // Registration fixes the tool deadlines and the text describing defaults;
199
- // everything a call acts on is read per operation, so a save on the config
200
- // page applies to the next call instead of the next reload.
209
+ // 注册时就固定了工具的截止时间与描述默认值的文案;一次调用真正用到的一切都是每次操作现读
210
+ // 的,所以配置页上的保存对下一次调用生效,而不是对下一次重载生效。
201
211
  const initial = readConfig(config)
202
212
  const names = engineNames()
203
213
 
204
214
  async function resolveWorkspace(exec, cfg) {
205
- if (cfg.workspaceRoot) return cfg.workspaceRoot
215
+ if (cfg.scopes.workspaceRoot) return cfg.scopes.workspaceRoot
206
216
  if (exec.agent && ctx.workingDirectory?.ensure) {
207
217
  try {
208
218
  return await ctx.workingDirectory.ensure(exec.agent, exec.signal)
@@ -239,11 +249,11 @@ export function createTools(ctx, config) {
239
249
  },
240
250
  engine: {
241
251
  type: 'string',
242
- description: `auto (default) or ${names.files.join(', ')}. Use auto unless the user asks for a specific engine.`,
252
+ description: `auto (default) follows the configured files backend; ${names.files.join(', ')} otherwise. Name one only when the user asks for a specific engine.`,
243
253
  },
244
254
  limit: {
245
255
  type: 'number',
246
- description: `Max results (default ${initial.maxResults}; max ${MAX_LIMIT}).`,
256
+ description: `Max results (default ${initial.limits.maxResults}; max ${MAX_LIMIT}).`,
247
257
  },
248
258
  },
249
259
  timeoutMs: initial.timeoutMs,
@@ -252,7 +262,7 @@ export function createTools(ctx, config) {
252
262
  return guard(async () => {
253
263
  const cfg = readConfig(config)
254
264
  const query = requireQuery(args.query)
255
- const engine = resolveFilesEngine(args.engine)
265
+ const engine = resolveFilesEngine(engineFor(args.engine, cfg.files.backend))
256
266
  const scope = args.scope || 'workspace'
257
267
  const workspace = await resolveWorkspace(exec, cfg)
258
268
  let dir
@@ -264,11 +274,11 @@ export function createTools(ctx, config) {
264
274
  return await engine.search({
265
275
  query,
266
276
  dir,
267
- limit: clampLimit(args.limit, cfg.maxResults),
268
- esPath: cfg.esPath,
277
+ limit: clampLimit(args.limit, cfg.limits.maxResults),
278
+ esPath: cfg.files.everything.esPath,
269
279
  timeoutMs: initial.timeoutMs,
270
280
  signal: exec.signal,
271
- encoding: cfg.esEncoding,
281
+ encoding: cfg.files.everything.esEncoding,
272
282
  })
273
283
  })
274
284
  },
@@ -303,11 +313,11 @@ export function createTools(ctx, config) {
303
313
  },
304
314
  engine: {
305
315
  type: 'string',
306
- description: `auto (default) or ${names.content.join(', ')}. Use auto unless the user asks for a specific engine.`,
316
+ description: `auto (default) follows the configured content backend; ${names.content.join(', ')} otherwise. Name one only when the user asks for a specific engine.`,
307
317
  },
308
318
  limit: {
309
319
  type: 'number',
310
- description: `Max files (default ${initial.maxResults}; max ${MAX_LIMIT}).`,
320
+ description: `Max files (default ${initial.limits.maxResults}; max ${MAX_LIMIT}).`,
311
321
  },
312
322
  },
313
323
  timeoutMs: initial.timeoutMs,
@@ -316,15 +326,15 @@ export function createTools(ctx, config) {
316
326
  return guard(async () => {
317
327
  const cfg = readConfig(config)
318
328
  const query = requireQuery(args.query)
319
- const engine = resolveContentEngine(args.engine)
329
+ const engine = resolveContentEngine(engineFor(args.engine, cfg.content.backend))
320
330
  const workspace = await resolveWorkspace(exec, cfg)
321
331
  const roots = rootsFor(args.scope || 'workspace', args.path, cfg, workspace)
322
332
  const result = await engine.search({
323
333
  query,
324
334
  roots,
325
335
  ext: args.ext,
326
- limit: clampLimit(args.limit, cfg.maxResults),
327
- url: cfg.anytxtUrl,
336
+ limit: clampLimit(args.limit, cfg.limits.maxResults),
337
+ url: cfg.content.anytxt.url,
328
338
  timeoutMs: initial.timeoutMs,
329
339
  signal: exec.signal,
330
340
  })
@@ -382,7 +392,7 @@ export function createTools(ctx, config) {
382
392
  const fragments = await engine.fragments({
383
393
  ref: args.ref,
384
394
  query,
385
- url: cfg.anytxtUrl,
395
+ url: cfg.content.anytxt.url,
386
396
  timeoutMs: initial.timeoutMs,
387
397
  signal: exec.signal,
388
398
  })
@@ -412,7 +422,7 @@ export function createTools(ctx, config) {
412
422
  },
413
423
  max: {
414
424
  type: 'number',
415
- description: `Max characters (default ${initial.readMaxChars}; max ${MAX_READ}).`,
425
+ description: `Max characters (default ${initial.limits.readMaxChars}; max ${MAX_READ}).`,
416
426
  },
417
427
  },
418
428
  timeoutMs: initial.timeoutMs,
@@ -423,7 +433,7 @@ export function createTools(ctx, config) {
423
433
  if (typeof args.ref !== 'string' || args.ref.trim().length === 0) {
424
434
  throw new Error('ref must be a non-empty string')
425
435
  }
426
- const max = clampCount(args.max, cfg.readMaxChars, MAX_READ)
436
+ const max = clampCount(args.max, cfg.limits.readMaxChars, MAX_READ)
427
437
  const offset = clampOffset(args.offset)
428
438
  const parsed = parseRef(args.ref)
429
439
  const requested = args.engine && args.engine !== 'auto' ? args.engine : parsed.engine
@@ -436,7 +446,7 @@ export function createTools(ctx, config) {
436
446
  }
437
447
  const text = await engine.read({
438
448
  ref: args.ref,
439
- url: cfg.anytxtUrl,
449
+ url: cfg.content.anytxt.url,
440
450
  timeoutMs: initial.timeoutMs,
441
451
  signal: exec.signal,
442
452
  })
@@ -463,21 +473,21 @@ export function createTools(ctx, config) {
463
473
  return guard(async () => {
464
474
  const cfg = readConfig(config)
465
475
  const engine = validateEngine(args.engine, STATUS_ENGINES)
466
- let workspace = cfg.workspaceRoot || ctx.workingDirectory?.defaultDirectory || process.cwd()
476
+ let workspace = cfg.scopes.workspaceRoot || ctx.workingDirectory?.defaultDirectory || process.cwd()
467
477
  try {
468
478
  workspace = await resolveWorkspace(exec, cfg)
469
479
  } catch {
470
- // Status must still report the engines even when the workspace is gone.
480
+ // 工作区没了也得把引擎报出来。
471
481
  }
472
482
 
473
- // The same probe the configuration page's Test control asks for.
483
+ // 与配置页测试按钮要的是同一个探测。
474
484
  const { everything, anytxt } = await probeEngines(config, {
475
485
  only: engine,
476
486
  workspace,
477
487
  signal: exec.signal,
478
488
  })
479
489
 
480
- const all = cfg.allRoots ? parseAllRoots(cfg.allRoots) : defaultAllRoots()
490
+ const all = cfg.scopes.allRoots ? parseAllRoots(cfg.scopes.allRoots) : defaultAllRoots()
481
491
  return {
482
492
  text: formatStatus({
483
493
  everything,
@@ -485,8 +495,8 @@ export function createTools(ctx, config) {
485
495
  engines: engineNames(),
486
496
  roots: {
487
497
  workspace,
488
- work: cfg.workRoot,
489
- personal: cfg.personalRoot,
498
+ work: cfg.scopes.workRoot,
499
+ personal: cfg.scopes.personalRoot,
490
500
  all,
491
501
  },
492
502
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishdivinity/dsh-local-search",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Local file and document search for DeepSeek Harness: Everything filenames plus Anytxt document contents, read-only, with a configuration page on the Plugins page.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -23,7 +23,8 @@
23
23
  ],
24
24
  "scripts": {
25
25
  "test": "node --test",
26
- "check": "node --check lib/index.js && node --check lib/tools.js && node --check lib/engines.js && node --check lib/ref.js && node --check lib/everything.js && node --check lib/anytxt.js && node --check lib/roots.js && node --check lib/format.js && node --check lib/errors.js && node --check lib/config.js && node --check lib/probe.js && node --check lib/route.js && node --check lib/client.js",
26
+ "check": "node --check lib/index.js && node --check lib/tools.js && node --check lib/engines.js && node --check lib/ref.js && node --check lib/everything.js && node --check lib/anytxt.js && node --check lib/roots.js && node --check lib/format.js && node --check lib/errors.js && node --check lib/config.js && node --check lib/schema.js && node --check lib/describe.js && node --check lib/bootstrap.js && node --check lib/limits.js && node --check lib/probe.js && node --check lib/route.js && node --check lib/client.js && node --check scripts/expand-release-links.mjs && node --check scripts/privacy-scan.mjs",
27
+ "privacy": "node scripts/privacy-scan.mjs",
27
28
  "pack:check": "npm pack --dry-run"
28
29
  },
29
30
  "keywords": [