@fishdivinity/dsh-local-search 0.2.1 → 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/README-zh.md +36 -22
- package/README.md +35 -21
- package/cordis.patch.yml +2 -1
- package/lib/anytxt.js +49 -35
- package/lib/bootstrap.js +201 -0
- package/lib/client.js +1174 -315
- package/lib/config.js +193 -49
- package/lib/describe.js +157 -0
- package/lib/engines.js +6 -6
- package/lib/errors.js +2 -3
- package/lib/everything.js +29 -32
- package/lib/format.js +16 -18
- package/lib/index.js +71 -38
- package/lib/limits.js +6 -7
- package/lib/probe.js +22 -28
- package/lib/ref.js +5 -5
- package/lib/roots.js +21 -24
- package/lib/route.js +252 -71
- package/lib/schema.js +130 -0
- package/lib/tools.js +45 -34
- package/package.json +3 -2
package/lib/tools.js
CHANGED
|
@@ -29,13 +29,12 @@ async function guard(fn) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* @param
|
|
36
|
-
* @param
|
|
37
|
-
* @
|
|
38
|
-
* @returns the usable count
|
|
32
|
+
* 把可选的数字参数收进 `[1, cap]`。非数字参数回落到配置的默认值,而不是让窗口变成 NaN
|
|
33
|
+
* ——那样会静默返回空结果。
|
|
34
|
+
* @param value - 模型发来的参数原样
|
|
35
|
+
* @param fallback - 不是有限数字时用的默认值
|
|
36
|
+
* @param cap - 闭区间上界
|
|
37
|
+
* @returns 可用的条数
|
|
39
38
|
*/
|
|
40
39
|
function clampCount(value, fallback, cap) {
|
|
41
40
|
const number = Number.isFinite(value) ? Math.floor(value) : fallback
|
|
@@ -64,6 +63,19 @@ function validateEngine(engine, allowed) {
|
|
|
64
63
|
return value
|
|
65
64
|
}
|
|
66
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
|
+
|
|
67
79
|
function presentSearchCall(label) {
|
|
68
80
|
return (args) => ({
|
|
69
81
|
card: 'generic',
|
|
@@ -188,20 +200,19 @@ const statusOutput = {
|
|
|
188
200
|
}
|
|
189
201
|
|
|
190
202
|
/**
|
|
191
|
-
*
|
|
203
|
+
* 构建并注册五个面向模型的工具。
|
|
192
204
|
* @param {import('@deepseek-ai/cordis').Context} ctx
|
|
193
205
|
* @param {object} config
|
|
194
|
-
* @returns {Array<() => void>}
|
|
206
|
+
* @returns {Array<() => void>} 工具的 disposer
|
|
195
207
|
*/
|
|
196
208
|
export function createTools(ctx, config) {
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
// page applies to the next call instead of the next reload.
|
|
209
|
+
// 注册时就固定了工具的截止时间与描述默认值的文案;一次调用真正用到的一切都是每次操作现读
|
|
210
|
+
// 的,所以配置页上的保存对下一次调用生效,而不是对下一次重载生效。
|
|
200
211
|
const initial = readConfig(config)
|
|
201
212
|
const names = engineNames()
|
|
202
213
|
|
|
203
214
|
async function resolveWorkspace(exec, cfg) {
|
|
204
|
-
if (cfg.workspaceRoot) return cfg.workspaceRoot
|
|
215
|
+
if (cfg.scopes.workspaceRoot) return cfg.scopes.workspaceRoot
|
|
205
216
|
if (exec.agent && ctx.workingDirectory?.ensure) {
|
|
206
217
|
try {
|
|
207
218
|
return await ctx.workingDirectory.ensure(exec.agent, exec.signal)
|
|
@@ -238,11 +249,11 @@ export function createTools(ctx, config) {
|
|
|
238
249
|
},
|
|
239
250
|
engine: {
|
|
240
251
|
type: 'string',
|
|
241
|
-
description: `auto (default)
|
|
252
|
+
description: `auto (default) follows the configured files backend; ${names.files.join(', ')} otherwise. Name one only when the user asks for a specific engine.`,
|
|
242
253
|
},
|
|
243
254
|
limit: {
|
|
244
255
|
type: 'number',
|
|
245
|
-
description: `Max results (default ${initial.maxResults}; max ${MAX_LIMIT}).`,
|
|
256
|
+
description: `Max results (default ${initial.limits.maxResults}; max ${MAX_LIMIT}).`,
|
|
246
257
|
},
|
|
247
258
|
},
|
|
248
259
|
timeoutMs: initial.timeoutMs,
|
|
@@ -251,7 +262,7 @@ export function createTools(ctx, config) {
|
|
|
251
262
|
return guard(async () => {
|
|
252
263
|
const cfg = readConfig(config)
|
|
253
264
|
const query = requireQuery(args.query)
|
|
254
|
-
const engine = resolveFilesEngine(args.engine)
|
|
265
|
+
const engine = resolveFilesEngine(engineFor(args.engine, cfg.files.backend))
|
|
255
266
|
const scope = args.scope || 'workspace'
|
|
256
267
|
const workspace = await resolveWorkspace(exec, cfg)
|
|
257
268
|
let dir
|
|
@@ -263,11 +274,11 @@ export function createTools(ctx, config) {
|
|
|
263
274
|
return await engine.search({
|
|
264
275
|
query,
|
|
265
276
|
dir,
|
|
266
|
-
limit: clampLimit(args.limit, cfg.maxResults),
|
|
267
|
-
esPath: cfg.esPath,
|
|
277
|
+
limit: clampLimit(args.limit, cfg.limits.maxResults),
|
|
278
|
+
esPath: cfg.files.everything.esPath,
|
|
268
279
|
timeoutMs: initial.timeoutMs,
|
|
269
280
|
signal: exec.signal,
|
|
270
|
-
encoding: cfg.esEncoding,
|
|
281
|
+
encoding: cfg.files.everything.esEncoding,
|
|
271
282
|
})
|
|
272
283
|
})
|
|
273
284
|
},
|
|
@@ -302,11 +313,11 @@ export function createTools(ctx, config) {
|
|
|
302
313
|
},
|
|
303
314
|
engine: {
|
|
304
315
|
type: 'string',
|
|
305
|
-
description: `auto (default)
|
|
316
|
+
description: `auto (default) follows the configured content backend; ${names.content.join(', ')} otherwise. Name one only when the user asks for a specific engine.`,
|
|
306
317
|
},
|
|
307
318
|
limit: {
|
|
308
319
|
type: 'number',
|
|
309
|
-
description: `Max files (default ${initial.maxResults}; max ${MAX_LIMIT}).`,
|
|
320
|
+
description: `Max files (default ${initial.limits.maxResults}; max ${MAX_LIMIT}).`,
|
|
310
321
|
},
|
|
311
322
|
},
|
|
312
323
|
timeoutMs: initial.timeoutMs,
|
|
@@ -315,15 +326,15 @@ export function createTools(ctx, config) {
|
|
|
315
326
|
return guard(async () => {
|
|
316
327
|
const cfg = readConfig(config)
|
|
317
328
|
const query = requireQuery(args.query)
|
|
318
|
-
const engine = resolveContentEngine(args.engine)
|
|
329
|
+
const engine = resolveContentEngine(engineFor(args.engine, cfg.content.backend))
|
|
319
330
|
const workspace = await resolveWorkspace(exec, cfg)
|
|
320
331
|
const roots = rootsFor(args.scope || 'workspace', args.path, cfg, workspace)
|
|
321
332
|
const result = await engine.search({
|
|
322
333
|
query,
|
|
323
334
|
roots,
|
|
324
335
|
ext: args.ext,
|
|
325
|
-
limit: clampLimit(args.limit, cfg.maxResults),
|
|
326
|
-
url: cfg.
|
|
336
|
+
limit: clampLimit(args.limit, cfg.limits.maxResults),
|
|
337
|
+
url: cfg.content.anytxt.url,
|
|
327
338
|
timeoutMs: initial.timeoutMs,
|
|
328
339
|
signal: exec.signal,
|
|
329
340
|
})
|
|
@@ -381,7 +392,7 @@ export function createTools(ctx, config) {
|
|
|
381
392
|
const fragments = await engine.fragments({
|
|
382
393
|
ref: args.ref,
|
|
383
394
|
query,
|
|
384
|
-
url: cfg.
|
|
395
|
+
url: cfg.content.anytxt.url,
|
|
385
396
|
timeoutMs: initial.timeoutMs,
|
|
386
397
|
signal: exec.signal,
|
|
387
398
|
})
|
|
@@ -411,7 +422,7 @@ export function createTools(ctx, config) {
|
|
|
411
422
|
},
|
|
412
423
|
max: {
|
|
413
424
|
type: 'number',
|
|
414
|
-
description: `Max characters (default ${initial.readMaxChars}; max ${MAX_READ}).`,
|
|
425
|
+
description: `Max characters (default ${initial.limits.readMaxChars}; max ${MAX_READ}).`,
|
|
415
426
|
},
|
|
416
427
|
},
|
|
417
428
|
timeoutMs: initial.timeoutMs,
|
|
@@ -422,7 +433,7 @@ export function createTools(ctx, config) {
|
|
|
422
433
|
if (typeof args.ref !== 'string' || args.ref.trim().length === 0) {
|
|
423
434
|
throw new Error('ref must be a non-empty string')
|
|
424
435
|
}
|
|
425
|
-
const max = clampCount(args.max, cfg.readMaxChars, MAX_READ)
|
|
436
|
+
const max = clampCount(args.max, cfg.limits.readMaxChars, MAX_READ)
|
|
426
437
|
const offset = clampOffset(args.offset)
|
|
427
438
|
const parsed = parseRef(args.ref)
|
|
428
439
|
const requested = args.engine && args.engine !== 'auto' ? args.engine : parsed.engine
|
|
@@ -435,7 +446,7 @@ export function createTools(ctx, config) {
|
|
|
435
446
|
}
|
|
436
447
|
const text = await engine.read({
|
|
437
448
|
ref: args.ref,
|
|
438
|
-
url: cfg.
|
|
449
|
+
url: cfg.content.anytxt.url,
|
|
439
450
|
timeoutMs: initial.timeoutMs,
|
|
440
451
|
signal: exec.signal,
|
|
441
452
|
})
|
|
@@ -462,21 +473,21 @@ export function createTools(ctx, config) {
|
|
|
462
473
|
return guard(async () => {
|
|
463
474
|
const cfg = readConfig(config)
|
|
464
475
|
const engine = validateEngine(args.engine, STATUS_ENGINES)
|
|
465
|
-
let workspace = cfg.workspaceRoot || ctx.workingDirectory?.defaultDirectory || process.cwd()
|
|
476
|
+
let workspace = cfg.scopes.workspaceRoot || ctx.workingDirectory?.defaultDirectory || process.cwd()
|
|
466
477
|
try {
|
|
467
478
|
workspace = await resolveWorkspace(exec, cfg)
|
|
468
479
|
} catch {
|
|
469
|
-
//
|
|
480
|
+
// 工作区没了也得把引擎报出来。
|
|
470
481
|
}
|
|
471
482
|
|
|
472
|
-
//
|
|
483
|
+
// 与配置页测试按钮要的是同一个探测。
|
|
473
484
|
const { everything, anytxt } = await probeEngines(config, {
|
|
474
485
|
only: engine,
|
|
475
486
|
workspace,
|
|
476
487
|
signal: exec.signal,
|
|
477
488
|
})
|
|
478
489
|
|
|
479
|
-
const all = cfg.allRoots ? parseAllRoots(cfg.allRoots) : defaultAllRoots()
|
|
490
|
+
const all = cfg.scopes.allRoots ? parseAllRoots(cfg.scopes.allRoots) : defaultAllRoots()
|
|
480
491
|
return {
|
|
481
492
|
text: formatStatus({
|
|
482
493
|
everything,
|
|
@@ -484,8 +495,8 @@ export function createTools(ctx, config) {
|
|
|
484
495
|
engines: engineNames(),
|
|
485
496
|
roots: {
|
|
486
497
|
workspace,
|
|
487
|
-
work: cfg.workRoot,
|
|
488
|
-
personal: cfg.personalRoot,
|
|
498
|
+
work: cfg.scopes.workRoot,
|
|
499
|
+
personal: cfg.scopes.personalRoot,
|
|
489
500
|
all,
|
|
490
501
|
},
|
|
491
502
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishdivinity/dsh-local-search",
|
|
3
|
-
"version": "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/limits.js && node --check lib/probe.js && node --check lib/route.js && node --check lib/client.js && node --check scripts/expand-release-links.mjs",
|
|
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": [
|