@dsh-plus/web-search-services 0.1.3 → 0.1.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.
- package/lib/index.d.ts +46 -42
- package/lib/index.js +48 -63
- package/package.json +12 -9
- package/src/config.ts +18 -7
- package/src/index.ts +10 -24
package/lib/index.d.ts
CHANGED
|
@@ -1,49 +1,53 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { UnwrapVolatile } from "@dsh-plus/shared";
|
|
2
2
|
import { WebSearchProvider, WebSearchRequest, WebSearchResult } from "@deepseek-ai/dsh-web";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
+
import z from "@deepseek-ai/schemastery";
|
|
4
5
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
6
|
//#region src/config.d.ts
|
|
6
7
|
declare const SETTINGS_NS = "dsh-plus-web-search-services";
|
|
7
|
-
declare const Config: z<Schemastery.ObjectS<{
|
|
8
|
-
scriptPath: z<string, string>;
|
|
9
|
-
envFile: z<string, string>;
|
|
10
|
-
python: z<string, string>;
|
|
11
|
-
priority: z<("exa" | "openai-chat" | "tavily")[]
|
|
12
|
-
timeoutMs: z<number, number>;
|
|
13
|
-
keys: z<Schemastery.ObjectS<{
|
|
14
|
-
tavily: z<string, string>;
|
|
15
|
-
exa: z<string, string>;
|
|
16
|
-
openaiApiKey: z<string, string>;
|
|
17
|
-
openaiBaseUrl: z<string, string>;
|
|
18
|
-
openaiModel: z<string, string>;
|
|
19
|
-
}
|
|
20
|
-
tavily: z<string, string>;
|
|
21
|
-
exa: z<string, string>;
|
|
22
|
-
openaiApiKey: z<string, string>;
|
|
23
|
-
openaiBaseUrl: z<string, string>;
|
|
24
|
-
openaiModel: z<string, string>;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
scriptPath: z<string, string>;
|
|
28
|
-
envFile: z<string, string>;
|
|
29
|
-
python: z<string, string>;
|
|
30
|
-
priority: z<("exa" | "openai-chat" | "tavily")[]
|
|
31
|
-
timeoutMs: z<number, number>;
|
|
32
|
-
keys: z<Schemastery.ObjectS<{
|
|
33
|
-
tavily: z<string, string>;
|
|
34
|
-
exa: z<string, string>;
|
|
35
|
-
openaiApiKey: z<string, string>;
|
|
36
|
-
openaiBaseUrl: z<string, string>;
|
|
37
|
-
openaiModel: z<string, string>;
|
|
38
|
-
}
|
|
39
|
-
tavily: z<string, string>;
|
|
40
|
-
exa: z<string, string>;
|
|
41
|
-
openaiApiKey: z<string, string>;
|
|
42
|
-
openaiBaseUrl: z<string, string>;
|
|
43
|
-
openaiModel: z<string, string>;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
8
|
+
declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
9
|
+
scriptPath: z<string, string, "volatile-defined">;
|
|
10
|
+
envFile: z<string, string, "volatile-defined">;
|
|
11
|
+
python: z<string, string, "volatile-defined">;
|
|
12
|
+
priority: z<NoInfer<("exa" | "openai-chat" | "tavily")[]>, NoInfer<("exa" | "openai-chat" | "tavily")[]>, "volatile-defined">;
|
|
13
|
+
timeoutMs: z<number, number, "volatile-defined">;
|
|
14
|
+
keys: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
15
|
+
tavily: z<string, string, "defined">;
|
|
16
|
+
exa: z<string, string, "defined">;
|
|
17
|
+
openaiApiKey: z<string, string, "defined">;
|
|
18
|
+
openaiBaseUrl: z<string, string, "defined">;
|
|
19
|
+
openaiModel: z<string, string, "defined">;
|
|
20
|
+
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
21
|
+
tavily: z<string, string, "defined">;
|
|
22
|
+
exa: z<string, string, "defined">;
|
|
23
|
+
openaiApiKey: z<string, string, "defined">;
|
|
24
|
+
openaiBaseUrl: z<string, string, "defined">;
|
|
25
|
+
openaiModel: z<string, string, "defined">;
|
|
26
|
+
}>>>, "volatile-defined">;
|
|
27
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
28
|
+
scriptPath: z<string, string, "volatile-defined">;
|
|
29
|
+
envFile: z<string, string, "volatile-defined">;
|
|
30
|
+
python: z<string, string, "volatile-defined">;
|
|
31
|
+
priority: z<NoInfer<("exa" | "openai-chat" | "tavily")[]>, NoInfer<("exa" | "openai-chat" | "tavily")[]>, "volatile-defined">;
|
|
32
|
+
timeoutMs: z<number, number, "volatile-defined">;
|
|
33
|
+
keys: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
34
|
+
tavily: z<string, string, "defined">;
|
|
35
|
+
exa: z<string, string, "defined">;
|
|
36
|
+
openaiApiKey: z<string, string, "defined">;
|
|
37
|
+
openaiBaseUrl: z<string, string, "defined">;
|
|
38
|
+
openaiModel: z<string, string, "defined">;
|
|
39
|
+
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
40
|
+
tavily: z<string, string, "defined">;
|
|
41
|
+
exa: z<string, string, "defined">;
|
|
42
|
+
openaiApiKey: z<string, string, "defined">;
|
|
43
|
+
openaiBaseUrl: z<string, string, "defined">;
|
|
44
|
+
openaiModel: z<string, string, "defined">;
|
|
45
|
+
}>>>, "volatile-defined">;
|
|
46
|
+
}>>, "plain">;
|
|
47
|
+
/** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
|
|
48
|
+
type WebSearchServicesConfigFields = Schemastery.TypeT<typeof Config>;
|
|
49
|
+
/** 平面配置形态(消费面的读取形态,由活动引用解包得到)。 */
|
|
50
|
+
type WebSearchServicesConfig = UnwrapVolatile<WebSearchServicesConfigFields>;
|
|
47
51
|
//#endregion
|
|
48
52
|
//#region src/runner.d.ts
|
|
49
53
|
type SpawnFn = typeof spawn;
|
|
@@ -67,6 +71,6 @@ declare class SearchServicesProvider implements WebSearchProvider {
|
|
|
67
71
|
declare const name = "dsh-plus-web-search-services";
|
|
68
72
|
/** 行级 inject:dsh-web 服务激活后才应用本行(provider 注册即需 ctx.web)。 */
|
|
69
73
|
declare const inject: readonly ['web'];
|
|
70
|
-
declare function apply(ctx: Context, config: WebSearchServicesConfig): void;
|
|
74
|
+
declare function apply(ctx: Context, config: WebSearchServicesConfig | WebSearchServicesConfigFields): void;
|
|
71
75
|
//#endregion
|
|
72
76
|
export { Config, PROVIDER_ID, SETTINGS_NS, SearchServicesProvider, apply, inject, name };
|
package/lib/index.js
CHANGED
|
@@ -1,55 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { unwrapVolatile } from "@dsh-plus/shared";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
|
-
|
|
9
|
-
//#region src/config.ts
|
|
10
|
-
/**
|
|
11
|
-
* 配置单一事实源:cordis 行级 Config(组合默认值,patch 层可覆盖)与 settings
|
|
12
|
-
* namespace(用户层 $DSH_HOME/settings.yaml,热生效)共用同一 schemastery schema。
|
|
13
|
-
*
|
|
14
|
-
* 密钥主来源是 search-services skill 的 env 文件(envFile,默认
|
|
15
|
-
* ~/.config/search-services/env),保持 skill 单事实源、零搬迁。行级 keys 仅为
|
|
16
|
-
* 覆盖逃生舱;按存储规范 role('secret') 字段只允许行级注入,settings 用户层勿填。
|
|
17
|
-
* @module web-search-services/config
|
|
18
|
-
*/
|
|
19
|
-
const SETTINGS_NS = "dsh-plus-web-search-services";
|
|
20
|
-
const DEFAULT_ENV_FILE = "~/.config/search-services/env";
|
|
21
|
-
const DEFAULT_TIMEOUT_MS = 55e3;
|
|
22
|
-
const DEFAULT_PRIORITY = [
|
|
23
|
-
"tavily",
|
|
24
|
-
"exa",
|
|
25
|
-
"openai-chat"
|
|
26
|
-
];
|
|
27
|
-
const PRIORITY_ENTRY = z.union([
|
|
28
|
-
"tavily",
|
|
29
|
-
"exa",
|
|
30
|
-
"openai-chat"
|
|
31
|
-
]);
|
|
32
|
-
const KeysSchema = z.object({
|
|
33
|
-
tavily: z.string().role("secret").description("Tavily API key(覆盖 envFile 的 TAVILY_API_KEY;行级注入专用)").default(""),
|
|
34
|
-
exa: z.string().role("secret").description("Exa API key(覆盖 envFile 的 EXA_API_KEY;行级注入专用)").default(""),
|
|
35
|
-
openaiApiKey: z.string().role("secret").description("OpenAI-compatible API key(覆盖 SEARCH_OPENAI_API_KEY;行级注入专用)").default(""),
|
|
36
|
-
openaiBaseUrl: z.string().description("OpenAI-compatible base URL(覆盖 SEARCH_OPENAI_BASE_URL)").default(""),
|
|
37
|
-
openaiModel: z.string().description("OpenAI-compatible 搜索模型名(覆盖 SEARCH_OPENAI_MODEL)").default("")
|
|
38
|
-
});
|
|
39
|
-
const Config = z.object({
|
|
40
|
-
scriptPath: z.string().description("search.py 路径(支持 ~ 展开);空串 = 使用随包内置脚本副本").default(""),
|
|
41
|
-
envFile: z.string().description("skill 密钥 env 文件路径(支持 ~ 展开);空串表示不读 envFile").default(DEFAULT_ENV_FILE),
|
|
42
|
-
python: z.string().description("python 可执行文件").default("python3"),
|
|
43
|
-
priority: z.array(PRIORITY_ENTRY).description("后端优先级:靠前的先尝试,失败自动回退到下一个").default([...DEFAULT_PRIORITY]),
|
|
44
|
-
timeoutMs: z.natural().min(1e3).description("单次搜索子进程超时(毫秒),应低于 dsh-tool-web 的 searchTimeoutMs").default(DEFAULT_TIMEOUT_MS),
|
|
45
|
-
keys: KeysSchema.default({
|
|
46
|
-
tavily: "",
|
|
47
|
-
exa: "",
|
|
48
|
-
openaiApiKey: "",
|
|
49
|
-
openaiBaseUrl: "",
|
|
50
|
-
openaiModel: ""
|
|
51
|
-
})
|
|
52
|
-
});
|
|
8
|
+
import z from "@deepseek-ai/schemastery";
|
|
53
9
|
/** 由 web 请求 + 插件配置生成 search.py 的 argv(不含解释器与脚本路径)。 */
|
|
54
10
|
function buildSearchArgv(cfg, request) {
|
|
55
11
|
const query = request.query.trim();
|
|
@@ -413,29 +369,58 @@ var SearchServicesProvider = class {
|
|
|
413
369
|
}
|
|
414
370
|
};
|
|
415
371
|
//#endregion
|
|
372
|
+
//#region src/config.ts
|
|
373
|
+
/**
|
|
374
|
+
* 配置单一事实源:cordis 行级 Config(组合默认值,patch 层可覆盖)与 settings
|
|
375
|
+
* namespace(用户层 $DSH_HOME/settings.yaml,热生效)共用同一 schemastery schema。
|
|
376
|
+
*
|
|
377
|
+
* 密钥主来源是 search-services skill 的 env 文件(envFile,默认
|
|
378
|
+
* ~/.config/search-services/env),保持 skill 单事实源、零搬迁。行级 keys 仅为
|
|
379
|
+
* 覆盖逃生舱;按存储规范 role('secret') 字段只允许行级注入,settings 用户层勿填。
|
|
380
|
+
* @module web-search-services/config
|
|
381
|
+
*/
|
|
382
|
+
const SETTINGS_NS = "dsh-plus-web-search-services";
|
|
383
|
+
const DEFAULT_ENV_FILE = "~/.config/search-services/env";
|
|
384
|
+
const DEFAULT_TIMEOUT_MS = 55e3;
|
|
385
|
+
const DEFAULT_PRIORITY = [
|
|
386
|
+
"tavily",
|
|
387
|
+
"exa",
|
|
388
|
+
"openai-chat"
|
|
389
|
+
];
|
|
390
|
+
const PRIORITY_ENTRY = z.union([
|
|
391
|
+
"tavily",
|
|
392
|
+
"exa",
|
|
393
|
+
"openai-chat"
|
|
394
|
+
]);
|
|
395
|
+
const KeysSchema = z.object({
|
|
396
|
+
tavily: z.string().role("secret").description("Tavily API key(覆盖 envFile 的 TAVILY_API_KEY;行级注入专用)").default(""),
|
|
397
|
+
exa: z.string().role("secret").description("Exa API key(覆盖 envFile 的 EXA_API_KEY;行级注入专用)").default(""),
|
|
398
|
+
openaiApiKey: z.string().role("secret").description("OpenAI-compatible API key(覆盖 SEARCH_OPENAI_API_KEY;行级注入专用)").default(""),
|
|
399
|
+
openaiBaseUrl: z.string().description("OpenAI-compatible base URL(覆盖 SEARCH_OPENAI_BASE_URL)").default(""),
|
|
400
|
+
openaiModel: z.string().description("OpenAI-compatible 搜索模型名(覆盖 SEARCH_OPENAI_MODEL)").default("")
|
|
401
|
+
});
|
|
402
|
+
const Config = z.object({
|
|
403
|
+
scriptPath: z.string().description("search.py 路径(支持 ~ 展开);空串 = 使用随包内置脚本副本").default("").volatile(),
|
|
404
|
+
envFile: z.string().description("skill 密钥 env 文件路径(支持 ~ 展开);空串表示不读 envFile").default(DEFAULT_ENV_FILE).volatile(),
|
|
405
|
+
python: z.string().description("python 可执行文件").default("python3").volatile(),
|
|
406
|
+
priority: z.array(PRIORITY_ENTRY).description("后端优先级:靠前的先尝试,失败自动回退到下一个").default([...DEFAULT_PRIORITY]).volatile(),
|
|
407
|
+
timeoutMs: z.natural().min(1e3).description("单次搜索子进程超时(毫秒),应低于 dsh-tool-web 的 searchTimeoutMs").default(DEFAULT_TIMEOUT_MS).volatile(),
|
|
408
|
+
keys: KeysSchema.default({
|
|
409
|
+
tavily: "",
|
|
410
|
+
exa: "",
|
|
411
|
+
openaiApiKey: "",
|
|
412
|
+
openaiBaseUrl: "",
|
|
413
|
+
openaiModel: ""
|
|
414
|
+
}).volatile()
|
|
415
|
+
});
|
|
416
|
+
//#endregion
|
|
416
417
|
//#region src/index.ts
|
|
417
418
|
const name = "dsh-plus-web-search-services";
|
|
418
419
|
/** 行级 inject:dsh-web 服务激活后才应用本行(provider 注册即需 ctx.web)。 */
|
|
419
420
|
const inject = ["web"];
|
|
420
421
|
function apply(ctx, config) {
|
|
421
|
-
const provider = new SearchServicesProvider(
|
|
422
|
+
const provider = new SearchServicesProvider(() => unwrapVolatile(config));
|
|
422
423
|
ctx.effect(() => ctx.web.registerSearchProvider(provider), "dsh-plus-web-search-services: register search provider");
|
|
423
424
|
}
|
|
424
|
-
/**
|
|
425
|
-
* 配置源:settings 用户层($DSH_HOME/settings.yaml,热生效)在时优先生效,
|
|
426
|
-
* 缺席/detach 回落 cordis 行级 config(官方 installSection 可选消费范式)。
|
|
427
|
-
*/
|
|
428
|
-
function configSource(ctx, rowConfig) {
|
|
429
|
-
let current = () => rowConfig;
|
|
430
|
-
ctx.inject(["settings"], (settingsCtx) => {
|
|
431
|
-
settingsCtx.settings.installSection(ctx, SETTINGS_NS, Config, rowConfig, {
|
|
432
|
-
setSource: (source) => {
|
|
433
|
-
current = source;
|
|
434
|
-
},
|
|
435
|
-
onChange: () => {}
|
|
436
|
-
});
|
|
437
|
-
});
|
|
438
|
-
return () => current();
|
|
439
|
-
}
|
|
440
425
|
//#endregion
|
|
441
426
|
export { Config, PROVIDER_ID, SETTINGS_NS, SearchServicesProvider, apply, inject, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/web-search-services",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "dsh-plus service plugin: web_search 免费后端聚合——经官方 ctx.web provider 扩展点桥接 search-services skill(Tavily/Exa/OpenAI Chat 免费额度,优先级+自动回退+key 配置),官方 web_search 工具与输出格式零修改",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -18,17 +18,20 @@
|
|
|
18
18
|
"src",
|
|
19
19
|
"scripts"
|
|
20
20
|
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@dsh-plus/shared": "0.1.23"
|
|
23
|
+
},
|
|
21
24
|
"peerDependencies": {
|
|
22
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
23
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
24
|
-
"@deepseek-ai/dsh-web": "^0.1.
|
|
25
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
25
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
26
|
+
"@deepseek-ai/dsh-settings": "^0.1.7-rc.1",
|
|
27
|
+
"@deepseek-ai/dsh-web": "^0.1.7-rc.1",
|
|
28
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
26
29
|
},
|
|
27
30
|
"devDependencies": {
|
|
28
|
-
"@deepseek-ai/cordis": "4.0.
|
|
29
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
30
|
-
"@deepseek-ai/dsh-web": "0.1.
|
|
31
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
31
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
32
|
+
"@deepseek-ai/dsh-settings": "0.1.7-rc.1",
|
|
33
|
+
"@deepseek-ai/dsh-web": "0.1.7-rc.1",
|
|
34
|
+
"@deepseek-ai/schemastery": "3.18.4"
|
|
32
35
|
},
|
|
33
36
|
"publishConfig": {
|
|
34
37
|
"access": "public"
|
package/src/config.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module web-search-services/config
|
|
9
9
|
*/
|
|
10
10
|
import z from '@deepseek-ai/schemastery'
|
|
11
|
+
import type { UnwrapVolatile } from '@dsh-plus/shared'
|
|
11
12
|
|
|
12
13
|
import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
|
|
13
14
|
|
|
@@ -49,33 +50,43 @@ const KeysSchema = z.object({
|
|
|
49
50
|
.default(''),
|
|
50
51
|
})
|
|
51
52
|
|
|
53
|
+
// 0.1.7:全字段 `.volatile()`——条目进入 settings describe 视图(卡片可读写;
|
|
54
|
+
// keys 内 role('secret') 字段由 describe/write 层做密文遮蔽,用户层勿填),
|
|
55
|
+
// loader 原位提交活动引用,current() 现取即热。
|
|
52
56
|
export const Config = z.object({
|
|
53
57
|
scriptPath: z
|
|
54
58
|
.string()
|
|
55
59
|
.description('search.py 路径(支持 ~ 展开);空串 = 使用随包内置脚本副本')
|
|
56
|
-
.default(DEFAULT_SCRIPT_PATH)
|
|
60
|
+
.default(DEFAULT_SCRIPT_PATH)
|
|
61
|
+
.volatile(),
|
|
57
62
|
envFile: z
|
|
58
63
|
.string()
|
|
59
64
|
.description('skill 密钥 env 文件路径(支持 ~ 展开);空串表示不读 envFile')
|
|
60
|
-
.default(DEFAULT_ENV_FILE)
|
|
61
|
-
|
|
65
|
+
.default(DEFAULT_ENV_FILE)
|
|
66
|
+
.volatile(),
|
|
67
|
+
python: z.string().description('python 可执行文件').default('python3').volatile(),
|
|
62
68
|
priority: z
|
|
63
69
|
.array(PRIORITY_ENTRY)
|
|
64
70
|
.description('后端优先级:靠前的先尝试,失败自动回退到下一个')
|
|
65
|
-
.default([...DEFAULT_PRIORITY])
|
|
71
|
+
.default([...DEFAULT_PRIORITY])
|
|
72
|
+
.volatile(),
|
|
66
73
|
timeoutMs: z
|
|
67
74
|
.natural()
|
|
68
75
|
.min(1000)
|
|
69
76
|
.description('单次搜索子进程超时(毫秒),应低于 dsh-tool-web 的 searchTimeoutMs')
|
|
70
|
-
.default(DEFAULT_TIMEOUT_MS)
|
|
77
|
+
.default(DEFAULT_TIMEOUT_MS)
|
|
78
|
+
.volatile(),
|
|
71
79
|
keys: KeysSchema.default({
|
|
72
80
|
tavily: '',
|
|
73
81
|
exa: '',
|
|
74
82
|
openaiApiKey: '',
|
|
75
83
|
openaiBaseUrl: '',
|
|
76
84
|
openaiModel: '',
|
|
77
|
-
}),
|
|
85
|
+
}).volatile(),
|
|
78
86
|
})
|
|
79
87
|
|
|
80
|
-
|
|
88
|
+
/** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
|
|
89
|
+
export type WebSearchServicesConfigFields = Schemastery.TypeT<typeof Config>
|
|
90
|
+
/** 平面配置形态(消费面的读取形态,由活动引用解包得到)。 */
|
|
91
|
+
export type WebSearchServicesConfig = UnwrapVolatile<WebSearchServicesConfigFields>
|
|
81
92
|
export type KeysConfig = WebSearchServicesConfig['keys']
|
package/src/index.ts
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis'
|
|
11
11
|
import type {} from '@deepseek-ai/dsh-settings'
|
|
12
12
|
import type {} from '@deepseek-ai/dsh-web'
|
|
13
|
+
import { unwrapVolatile } from '@dsh-plus/shared'
|
|
13
14
|
|
|
14
|
-
import {
|
|
15
|
+
import type { WebSearchServicesConfig, WebSearchServicesConfigFields } from './config.ts'
|
|
15
16
|
import { SearchServicesProvider } from './provider.ts'
|
|
16
17
|
|
|
17
18
|
export const name = 'dsh-plus-web-search-services'
|
|
@@ -19,33 +20,18 @@ export const name = 'dsh-plus-web-search-services'
|
|
|
19
20
|
/** 行级 inject:dsh-web 服务激活后才应用本行(provider 注册即需 ctx.web)。 */
|
|
20
21
|
export const inject = ['web'] as const
|
|
21
22
|
|
|
23
|
+
export { Config, SETTINGS_NS } from './config.ts'
|
|
22
24
|
export { PROVIDER_ID, SearchServicesProvider } from './provider.ts'
|
|
23
|
-
export { Config, SETTINGS_NS }
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
// config 运行期为 loader 解析的 volatile 活动字段(用户层 override 并入行级
|
|
27
|
+
// config,写入原位提交)——每次读现取平面快照,替代 0.1.6 installSection/setSource。
|
|
28
|
+
export function apply(
|
|
29
|
+
ctx: Context,
|
|
30
|
+
config: WebSearchServicesConfig | WebSearchServicesConfigFields,
|
|
31
|
+
): void {
|
|
32
|
+
const provider = new SearchServicesProvider(() => unwrapVolatile(config))
|
|
27
33
|
ctx.effect(
|
|
28
34
|
() => ctx.web.registerSearchProvider(provider),
|
|
29
35
|
'dsh-plus-web-search-services: register search provider',
|
|
30
36
|
)
|
|
31
37
|
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 配置源:settings 用户层($DSH_HOME/settings.yaml,热生效)在时优先生效,
|
|
35
|
-
* 缺席/detach 回落 cordis 行级 config(官方 installSection 可选消费范式)。
|
|
36
|
-
*/
|
|
37
|
-
function configSource(
|
|
38
|
-
ctx: Context,
|
|
39
|
-
rowConfig: WebSearchServicesConfig,
|
|
40
|
-
): () => WebSearchServicesConfig {
|
|
41
|
-
let current: () => WebSearchServicesConfig = () => rowConfig
|
|
42
|
-
ctx.inject(['settings'], (settingsCtx) => {
|
|
43
|
-
settingsCtx.settings.installSection(ctx, SETTINGS_NS, Config, rowConfig, {
|
|
44
|
-
setSource: (source) => {
|
|
45
|
-
current = source
|
|
46
|
-
},
|
|
47
|
-
onChange: () => {},
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
return () => current()
|
|
51
|
-
}
|