@dsh-plus/web-search-services 0.1.3 → 0.1.4

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 CHANGED
@@ -1,49 +1,53 @@
1
- import z from "@deepseek-ai/schemastery";
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")[], ("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
- }>, Schemastery.ObjectT<{
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
- }>, Schemastery.ObjectT<{
27
- scriptPath: z<string, string>;
28
- envFile: z<string, string>;
29
- python: z<string, string>;
30
- priority: z<("exa" | "openai-chat" | "tavily")[], ("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
- }>, Schemastery.ObjectT<{
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
- type WebSearchServicesConfig = Schemastery.TypeT<typeof Config>;
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 z from "@deepseek-ai/schemastery";
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
- //#endregion
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(configSource(ctx, config));
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",
3
+ "version": "0.1.4",
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.22"
23
+ },
21
24
  "peerDependencies": {
22
- "@deepseek-ai/cordis": "^4.0.2",
23
- "@deepseek-ai/dsh-settings": "^0.1.6-alpha.2",
24
- "@deepseek-ai/dsh-web": "^0.1.6-alpha.2",
25
- "@deepseek-ai/schemastery": "^3.18.2"
25
+ "@deepseek-ai/cordis": "^4.0.4",
26
+ "@deepseek-ai/dsh-settings": "^0.1.7-alpha.2",
27
+ "@deepseek-ai/dsh-web": "^0.1.7-alpha.2",
28
+ "@deepseek-ai/schemastery": "^3.18.4"
26
29
  },
27
30
  "devDependencies": {
28
- "@deepseek-ai/cordis": "4.0.2",
29
- "@deepseek-ai/dsh-settings": "0.1.6-alpha.2",
30
- "@deepseek-ai/dsh-web": "0.1.6-alpha.2",
31
- "@deepseek-ai/schemastery": "3.18.2"
31
+ "@deepseek-ai/cordis": "4.0.4",
32
+ "@deepseek-ai/dsh-settings": "0.1.7-alpha.2",
33
+ "@deepseek-ai/dsh-web": "0.1.7-alpha.2",
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
- python: z.string().description('python 可执行文件').default('python3'),
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
- export type WebSearchServicesConfig = Schemastery.TypeT<typeof Config>
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 { Config, SETTINGS_NS, type WebSearchServicesConfig } from './config.ts'
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
- export function apply(ctx: Context, config: WebSearchServicesConfig): void {
26
- const provider = new SearchServicesProvider(configSource(ctx, config))
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
- }