@dcrays/web-search 0.1.1 → 0.1.2

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.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # @dcrays/web-search
2
2
 
3
- 书灵墨宝的 DSH 联网搜索插件。加载后注册 `web_search` 工具,把模型搜索请求映射到配置的 HTTP API。
3
+ 书灵墨宝的 DSH 联网搜索插件。加载后注册 `web_search` 工具,把模型搜索请求映射到搜索网关。
4
4
 
5
- 本包对应 `mbh-chat` 环境,端点地址从 Cordis 插件配置读取。
5
+ 单一发布包同时适用于生产与测试桌面:运行时根据 `mobook.json` 中记录的环境选择搜索网关与 MBHChat 凭据通道;`endpointUrl` 可在 Cordis 插件配置中覆盖。
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dcrays/web-search",
3
- "version": "0.1.1",
4
- "description": "DeepSeek Harness web search plugin for Mobook (production)",
3
+ "version": "0.1.2",
4
+ "description": "DeepSeek Harness web search plugin for Mobook",
5
5
  "type": "module",
6
6
  "main": "plugin/index.js",
7
7
  "types": "plugin/index.d.ts",
package/plugin/index.d.ts CHANGED
@@ -2,7 +2,6 @@ import type { Context } from '@deepseek-ai/cordis';
2
2
  import { Service } from '@deepseek-ai/cordis';
3
3
  import z from '@deepseek-ai/schemastery';
4
4
  import { type WebSearchRequest, type WebSearchResult } from '../src/index.js';
5
- export { DEFAULT_BUILD_ENV } from '../src/config/env-config.js';
6
5
  export declare const name = "mbh-web-search";
7
6
  export declare const inject: string[];
8
7
  export interface Config {
package/plugin/index.js CHANGED
@@ -9,6 +9,25 @@ import z from "@deepseek-ai/schemastery";
9
9
  // dist-npm/src/api-client.js
10
10
  import { createHmac, randomUUID } from "node:crypto";
11
11
  import { readFile } from "node:fs/promises";
12
+
13
+ // dist-npm/src/config/env-config.js
14
+ var ENV_CONFIG = {
15
+ production: {
16
+ channelId: "mbh-chat",
17
+ legacyChannelId: "mbhchat",
18
+ endpointUrl: "https://api-gateway.shuwenda.com/apikey-manage/api/search"
19
+ },
20
+ test: {
21
+ channelId: "mbh-chat-test",
22
+ legacyChannelId: "mbhchat-test",
23
+ endpointUrl: "https://api-gateway.shuwenda.icu/apikey-manage/api/search"
24
+ }
25
+ };
26
+ function resolveWebSearchEnvironment(value) {
27
+ return value === "production" ? "production" : "test";
28
+ }
29
+
30
+ // dist-npm/src/api-client.js
12
31
  var WebSearchApiError = class extends Error {
13
32
  code;
14
33
  constructor(code, message, options) {
@@ -125,23 +144,41 @@ function parseResponse(text, fallbackQuery, service) {
125
144
  }
126
145
  return result;
127
146
  }
147
+ function toEndpoint(endpointUrl) {
148
+ let endpoint;
149
+ try {
150
+ endpoint = new URL(endpointUrl);
151
+ } catch (error) {
152
+ throw new WebSearchApiError("INVALID_CONFIG", "\u641C\u7D22\u670D\u52A1\u5730\u5740\u914D\u7F6E\u4E0D\u6B63\u786E\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u68C0\u67E5\u63D2\u4EF6\u914D\u7F6E\u3002", { cause: error });
153
+ }
154
+ if (!["http:", "https:"].includes(endpoint.protocol)) {
155
+ throw new WebSearchApiError("INVALID_CONFIG", "\u641C\u7D22\u670D\u52A1\u5730\u5740\u4EC5\u652F\u6301 HTTP \u6216 HTTPS\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u68C0\u67E5\u63D2\u4EF6\u914D\u7F6E\u3002");
156
+ }
157
+ return endpoint;
158
+ }
128
159
  var WebSearchApiClient = class {
129
160
  config;
130
- endpoint;
161
+ environments;
131
162
  fetchImpl;
132
163
  readFileImpl;
133
164
  nowSeconds;
134
165
  nonce;
135
166
  constructor(config, dependencies = {}) {
136
167
  this.config = config;
137
- try {
138
- this.endpoint = new URL(config.endpointUrl);
139
- } catch (error) {
140
- throw new WebSearchApiError("INVALID_CONFIG", "\u641C\u7D22\u670D\u52A1\u5730\u5740\u914D\u7F6E\u4E0D\u6B63\u786E\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u68C0\u67E5\u63D2\u4EF6\u914D\u7F6E\u3002", { cause: error });
141
- }
142
- if (!["http:", "https:"].includes(this.endpoint.protocol)) {
143
- throw new WebSearchApiError("INVALID_CONFIG", "\u641C\u7D22\u670D\u52A1\u5730\u5740\u4EC5\u652F\u6301 HTTP \u6216 HTTPS\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u68C0\u67E5\u63D2\u4EF6\u914D\u7F6E\u3002");
144
- }
168
+ const environments = config.environments ?? ENV_CONFIG;
169
+ const override = config.endpointOverride?.trim();
170
+ this.environments = {
171
+ production: {
172
+ endpoint: toEndpoint(override || environments.production.endpointUrl),
173
+ channelId: environments.production.channelId,
174
+ legacyChannelId: environments.production.legacyChannelId
175
+ },
176
+ test: {
177
+ endpoint: toEndpoint(override || environments.test.endpointUrl),
178
+ channelId: environments.test.channelId,
179
+ legacyChannelId: environments.test.legacyChannelId
180
+ }
181
+ };
145
182
  for (const [name2, value] of [
146
183
  ["timeoutMs", config.timeoutMs],
147
184
  ["maxResponseBytes", config.maxResponseBytes],
@@ -178,7 +215,9 @@ var WebSearchApiClient = class {
178
215
  } catch (error) {
179
216
  throw new WebSearchApiError("MISSING_CREDENTIALS", "\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u58A8\u5B9D\u8FD0\u884C\u914D\u7F6E\uFF0C\u8BF7\u786E\u8BA4\u5BA2\u6237\u7AEF\u5DF2\u6B63\u5E38\u521D\u59CB\u5316\u540E\u91CD\u8BD5\u3002", { cause: error });
180
217
  }
181
- const credentials = resolveSearchCredentials(metadata, this.config.channelId, this.config.legacyChannelId);
218
+ const environment = resolveWebSearchEnvironment(record(metadata)?.environment);
219
+ const resolved = this.environments[environment];
220
+ const credentials = resolveSearchCredentials(metadata, resolved.channelId, resolved.legacyChannelId);
182
221
  const call = async (service) => {
183
222
  const timestamp = String(this.nowSeconds());
184
223
  const nonce = this.nonce();
@@ -207,7 +246,7 @@ ${nonce}`;
207
246
  const effectiveSignal = requestSignal(signal, this.config.timeoutMs);
208
247
  let response;
209
248
  try {
210
- response = await this.fetchImpl(this.endpoint, {
249
+ response = await this.fetchImpl(resolved.endpoint, {
211
250
  method: "POST",
212
251
  headers: { accept: "application/json", "content-type": "application/json" },
213
252
  body: JSON.stringify(body),
@@ -277,24 +316,6 @@ function createWebSearchTool(search) {
277
316
  // dist-npm/src/index.js
278
317
  var MBH_WEB_SEARCH_SERVICE_KEY = "mbhWebSearch";
279
318
 
280
- // dist-npm/src/config/env-config.js
281
- var ENV_CONFIG = {
282
- production: {
283
- packageName: "@dcrays/web-search",
284
- channelId: "mbh-chat",
285
- legacyChannelId: "mbhchat",
286
- endpointUrl: "https://api-gateway.shuwenda.com/apikey-manage/api/search"
287
- },
288
- test: {
289
- packageName: "@dcrays/web-search-test",
290
- channelId: "mbh-chat-test",
291
- legacyChannelId: "mbhchat-test",
292
- endpointUrl: "https://api-gateway.shuwenda.icu/apikey-manage/api/search"
293
- }
294
- };
295
- var DEFAULT_BUILD_ENV = "production";
296
- var ACTIVE_ENV_CONFIG = ENV_CONFIG[DEFAULT_BUILD_ENV];
297
-
298
319
  // dist-npm/plugin/index.js
299
320
  var name = "@dcrays/web-search";
300
321
  var inject = ["tools"];
@@ -316,10 +337,8 @@ var MbhWebSearchService = class extends Service {
316
337
  if (!config.enabled)
317
338
  return;
318
339
  this.operations = new WebSearchApiClient({
319
- endpointUrl: config.endpointUrl.trim() || ACTIVE_ENV_CONFIG.endpointUrl,
320
340
  metadataPath: resolveMetadataPath(config.metadataPath),
321
- channelId: ACTIVE_ENV_CONFIG.channelId,
322
- legacyChannelId: ACTIVE_ENV_CONFIG.legacyChannelId,
341
+ ...config.endpointUrl.trim() ? { endpointOverride: config.endpointUrl.trim() } : {},
323
342
  timeoutMs: config.timeoutMs,
324
343
  maxResponseBytes: config.maxResponseBytes,
325
344
  defaultCount: config.defaultCount,
@@ -345,13 +364,12 @@ function apply(ctx, config) {
345
364
  if (!service.isEnabled())
346
365
  return () => void 0;
347
366
  const unregister = ctx.tools.register(createWebSearchTool(service));
348
- ctx.logger(name).info("%s plugin loaded with web_search tool for %s", name, DEFAULT_BUILD_ENV);
367
+ ctx.logger(name).info("%s plugin loaded with web_search tool", name);
349
368
  return unregister;
350
369
  }, `${name} lifecycle`);
351
370
  }
352
371
  export {
353
372
  Config,
354
- DEFAULT_BUILD_ENV,
355
373
  MbhWebSearchService,
356
374
  apply,
357
375
  inject,
@@ -1,3 +1,4 @@
1
+ import { type EnvConfig, type WebSearchEnvironment } from './config/env-config.js';
1
2
  export interface WebSearchRequest {
2
3
  query: string;
3
4
  count?: number;
@@ -24,10 +25,9 @@ export interface WebSearchOperations {
24
25
  search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;
25
26
  }
26
27
  export interface WebSearchApiClientConfig {
27
- endpointUrl: string;
28
28
  metadataPath: string;
29
- channelId: string;
30
- legacyChannelId: string;
29
+ endpointOverride?: string;
30
+ environments?: Record<WebSearchEnvironment, EnvConfig>;
31
31
  timeoutMs: number;
32
32
  maxResponseBytes: number;
33
33
  defaultCount: number;
@@ -53,7 +53,7 @@ interface SearchCredentials {
53
53
  export declare function resolveSearchCredentials(metadata: unknown, channelId: string, legacyChannelId: string): SearchCredentials;
54
54
  export declare class WebSearchApiClient implements WebSearchOperations {
55
55
  private readonly config;
56
- private readonly endpoint;
56
+ private readonly environments;
57
57
  private readonly fetchImpl;
58
58
  private readonly readFileImpl;
59
59
  private readonly nowSeconds;
package/src/api-client.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createHmac, randomUUID } from 'node:crypto';
2
2
  import { readFile } from 'node:fs/promises';
3
+ import { ENV_CONFIG, resolveWebSearchEnvironment } from './config/env-config.js';
3
4
  export class WebSearchApiError extends Error {
4
5
  code;
5
6
  constructor(code, message, options) {
@@ -118,24 +119,42 @@ function parseResponse(text, fallbackQuery, service) {
118
119
  }
119
120
  return result;
120
121
  }
122
+ function toEndpoint(endpointUrl) {
123
+ let endpoint;
124
+ try {
125
+ endpoint = new URL(endpointUrl);
126
+ }
127
+ catch (error) {
128
+ throw new WebSearchApiError('INVALID_CONFIG', '搜索服务地址配置不正确,请联系管理员检查插件配置。', { cause: error });
129
+ }
130
+ if (!['http:', 'https:'].includes(endpoint.protocol)) {
131
+ throw new WebSearchApiError('INVALID_CONFIG', '搜索服务地址仅支持 HTTP 或 HTTPS,请联系管理员检查插件配置。');
132
+ }
133
+ return endpoint;
134
+ }
121
135
  export class WebSearchApiClient {
122
136
  config;
123
- endpoint;
137
+ environments;
124
138
  fetchImpl;
125
139
  readFileImpl;
126
140
  nowSeconds;
127
141
  nonce;
128
142
  constructor(config, dependencies = {}) {
129
143
  this.config = config;
130
- try {
131
- this.endpoint = new URL(config.endpointUrl);
132
- }
133
- catch (error) {
134
- throw new WebSearchApiError('INVALID_CONFIG', '搜索服务地址配置不正确,请联系管理员检查插件配置。', { cause: error });
135
- }
136
- if (!['http:', 'https:'].includes(this.endpoint.protocol)) {
137
- throw new WebSearchApiError('INVALID_CONFIG', '搜索服务地址仅支持 HTTP 或 HTTPS,请联系管理员检查插件配置。');
138
- }
144
+ const environments = config.environments ?? ENV_CONFIG;
145
+ const override = config.endpointOverride?.trim();
146
+ this.environments = {
147
+ production: {
148
+ endpoint: toEndpoint(override || environments.production.endpointUrl),
149
+ channelId: environments.production.channelId,
150
+ legacyChannelId: environments.production.legacyChannelId
151
+ },
152
+ test: {
153
+ endpoint: toEndpoint(override || environments.test.endpointUrl),
154
+ channelId: environments.test.channelId,
155
+ legacyChannelId: environments.test.legacyChannelId
156
+ }
157
+ };
139
158
  for (const [name, value] of [
140
159
  ['timeoutMs', config.timeoutMs],
141
160
  ['maxResponseBytes', config.maxResponseBytes],
@@ -173,7 +192,9 @@ export class WebSearchApiClient {
173
192
  catch (error) {
174
193
  throw new WebSearchApiError('MISSING_CREDENTIALS', '暂时无法读取墨宝运行配置,请确认客户端已正常初始化后重试。', { cause: error });
175
194
  }
176
- const credentials = resolveSearchCredentials(metadata, this.config.channelId, this.config.legacyChannelId);
195
+ const environment = resolveWebSearchEnvironment(record(metadata)?.environment);
196
+ const resolved = this.environments[environment];
197
+ const credentials = resolveSearchCredentials(metadata, resolved.channelId, resolved.legacyChannelId);
177
198
  const call = async (service) => {
178
199
  const timestamp = String(this.nowSeconds());
179
200
  const nonce = this.nonce();
@@ -199,7 +220,7 @@ export class WebSearchApiClient {
199
220
  const effectiveSignal = requestSignal(signal, this.config.timeoutMs);
200
221
  let response;
201
222
  try {
202
- response = await this.fetchImpl(this.endpoint, {
223
+ response = await this.fetchImpl(resolved.endpoint, {
203
224
  method: 'POST',
204
225
  headers: { accept: 'application/json', 'content-type': 'application/json' },
205
226
  body: JSON.stringify(body),
@@ -1,10 +1,15 @@
1
- export type BuildEnv = 'production' | 'test';
1
+ export type WebSearchEnvironment = 'production' | 'test';
2
2
  export interface EnvConfig {
3
- packageName: string;
4
3
  channelId: string;
5
4
  legacyChannelId: string;
6
5
  endpointUrl: string;
7
6
  }
8
- export declare const ENV_CONFIG: Record<BuildEnv, EnvConfig>;
9
- export declare const DEFAULT_BUILD_ENV: BuildEnv;
10
- export declare const ACTIVE_ENV_CONFIG: EnvConfig;
7
+ export declare const PACKAGE_NAME = "@dcrays/web-search";
8
+ export declare const ENV_CONFIG: Record<WebSearchEnvironment, EnvConfig>;
9
+ /**
10
+ * Map the desktop runtime environment recorded in mobook.json to the search
11
+ * environment. Development desktops share the test gateway and MBHChat channel.
12
+ * Missing/unknown values fall back to test so a misconfigured desktop never
13
+ * silently reaches the production gateway with test credentials.
14
+ */
15
+ export declare function resolveWebSearchEnvironment(value: unknown): WebSearchEnvironment;
@@ -1,16 +1,22 @@
1
+ export const PACKAGE_NAME = '@dcrays/web-search';
1
2
  export const ENV_CONFIG = {
2
3
  production: {
3
- packageName: '@dcrays/web-search',
4
4
  channelId: 'mbh-chat',
5
5
  legacyChannelId: 'mbhchat',
6
6
  endpointUrl: 'https://api-gateway.shuwenda.com/apikey-manage/api/search'
7
7
  },
8
8
  test: {
9
- packageName: '@dcrays/web-search-test',
10
9
  channelId: 'mbh-chat-test',
11
10
  legacyChannelId: 'mbhchat-test',
12
11
  endpointUrl: 'https://api-gateway.shuwenda.icu/apikey-manage/api/search'
13
12
  }
14
13
  };
15
- export const DEFAULT_BUILD_ENV = 'production';
16
- export const ACTIVE_ENV_CONFIG = ENV_CONFIG[DEFAULT_BUILD_ENV];
14
+ /**
15
+ * Map the desktop runtime environment recorded in mobook.json to the search
16
+ * environment. Development desktops share the test gateway and MBHChat channel.
17
+ * Missing/unknown values fall back to test so a misconfigured desktop never
18
+ * silently reaches the production gateway with test credentials.
19
+ */
20
+ export function resolveWebSearchEnvironment(value) {
21
+ return value === 'production' ? 'production' : 'test';
22
+ }