dsh-web-fetch-enhanced 0.0.4 → 0.0.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/README.en.md CHANGED
@@ -30,11 +30,12 @@ With an empty CIDR allowlist, the security boundary remains equivalent to the na
30
30
  - **Anonymous bounded GET requests** — no cookies, Authorization header, or URL credentials;
31
31
  - **Resource limits** — URL length, response bytes, decoded characters, redirects, and time are bounded;
32
32
  - **IPv4 and IPv6 coverage** — including IPv4-mapped IPv6 and active DNS64 / NAT64 checks;
33
- - **Outbound HTTP proxy alignment** — seamlessly respects DSH global proxy routes (`HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY`), while refusing unvalidated private IP literals from bypassing address checks through the proxy.
33
+ - **Outbound HTTP proxy alignment** — seamlessly respects DSH global proxy routes (`HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY`), while refusing unvalidated private IP literals from bypassing address checks through the proxy;
34
+ - **Dynamic system prompt alignment** — automatically injects explicit authorization guidance into the system prompt when non-public allowlists are configured, eliminating LLM refusal hallucinations when fetching authorized private destinations.
34
35
 
35
36
  ## Compatibility & Support Policy
36
37
 
37
- - **Minimum Supported DSH Version**: `0.1.2-rc.1`
38
+ - **Minimum Supported DSH Version**: `0.1.5-rc.2`
38
39
  - **Support Policy**: This plugin **only supports DeepSeek Harness RC (Release Candidate) releases and future stable releases**. Compatibility is not maintained for rapid-moving Alpha or development snapshot versions.
39
40
 
40
41
  ## Quick start
package/README.md CHANGED
@@ -30,11 +30,12 @@ DeepSeek Harness 原生 HTTP provider 默认拒绝所有非公网地址,这是
30
30
  - **受限匿名请求**:只发送无 Cookie、无 Authorization、无 URL 凭据的 GET 请求;
31
31
  - **完整资源上限**:限制 URL、响应字节、解码字符、重定向次数和超时时间;
32
32
  - **IPv4 / IPv6 防护**:覆盖 IPv4-mapped IPv6 与活动 DNS64 / NAT64 目标检查;
33
- - **全局出站代理协同**:无缝对接 DSH 全局 HTTP 代理路由(`HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY`),同时严格禁止非公网 IP 字面量越权走代理,杜绝内网 SSRF 风险。
33
+ - **全局出站代理协同**:无缝对接 DSH 全局 HTTP 代理路由(`HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY`),同时严格禁止非公网 IP 字面量越权走代理,杜绝内网 SSRF 风险;
34
+ - **动态系统提示词协同**:当管理员配置了非公网白名单时,自动向模型系统提示词注入显式授权声明,消除模型因默认安全准则而产生“无法访问内网”的拒答幻觉。
34
35
 
35
36
  ## 版本兼容性与支持策略
36
37
 
37
- - **最低支持的 DSH 版本**:`0.1.2-rc.1`
38
+ - **最低支持的 DSH 版本**:`0.1.5-rc.2`
38
39
  - **版本支持策略**:本插件**仅对 DeepSeek Harness 的 RC(Release Candidate)候选发布版本及后续稳定正式版提供支持**。由于 Alpha 或开发快照版本更迭频繁且缺乏稳定的 API 保证,本插件不再对 Alpha 等非 RC 阶段版本进行维护与适配。
39
40
 
40
41
  ## 快速开始
package/lib/index.js CHANGED
@@ -4,6 +4,7 @@ import ipaddr from "ipaddr.js";
4
4
  import { WebError } from "@deepseek-ai/dsh-web";
5
5
  import { deadline, timeoutOf } from "@deepseek-ai/dsh-timeout";
6
6
  import { lookup } from "node:dns/promises";
7
+ import { proxyRouteFor } from "@deepseek-ai/dsh-http-proxy";
7
8
  //#region lib/types/address-policy.js
8
9
  /**
9
10
  * Immutable address policy. Public unicast remains allowed. A non-public address
@@ -223,20 +224,14 @@ function embeddedIpv4Address(bytes, prefixLength) {
223
224
  return [...bytes.slice(prefixBytes, prefixBytes + beforeReservedOctet), ...bytes.slice(9, 13 - beforeReservedOctet)].join(".");
224
225
  }
225
226
  /**
226
- * Default proxy route resolver: safely resolves proxy route via @deepseek-ai/dsh-http-proxy if available,
227
- * gracefully degrading to direct routing if the module is absent.
227
+ * Default proxy route resolver: resolves proxy route via @deepseek-ai/dsh-http-proxy.
228
228
  */
229
- async function defaultProxyRoute(url) {
230
- try {
231
- const { proxyRouteFor } = await import("@deepseek-ai/dsh-http-proxy");
232
- const route = proxyRouteFor(url);
233
- return route.proxied ? {
234
- proxied: true,
235
- dispatcher: route.dispatcher
236
- } : { proxied: false };
237
- } catch {
238
- return { proxied: false };
239
- }
229
+ function defaultProxyRoute(url) {
230
+ const route = proxyRouteFor(url);
231
+ return route.proxied ? {
232
+ proxied: true,
233
+ dispatcher: route.dispatcher
234
+ } : { proxied: false };
240
235
  }
241
236
  /** Fetch while preserving the URL hostname for Host and TLS SNI. */
242
237
  async function requestPinned(url, addresses, headers, signal) {
@@ -628,6 +623,16 @@ const Config = z.object({
628
623
  maxRedirects: z.number().default(5),
629
624
  userAgent: z.string().default(DEFAULT_USER_AGENT)
630
625
  });
626
+ /** Build prompt guidance copy informing the model of authorized non-public destinations. */
627
+ function formatAllowlistPrompt(config) {
628
+ const cidrs = config.allowCidrs ?? [];
629
+ const hostnames = config.allowHostnames ?? [];
630
+ if (cidrs.length === 0 && hostnames.length === 0) return "";
631
+ const items = [];
632
+ if (cidrs.length > 0) items.push(`CIDRs: ${cidrs.join(", ")}`);
633
+ if (hostnames.length > 0) items.push(`hostnames: ${hostnames.join(", ")}`);
634
+ return `The operator has explicitly authorized web_fetch access to the following non-public destinations: [${items.join("; ")}]. You can safely fetch these endpoints.`;
635
+ }
631
636
  /** Construct the provider without mounting it, useful for tests and custom compositions. */
632
637
  function createProvider(config = {}, proxyResolver) {
633
638
  const resolved = resolveConfig(config);
@@ -664,7 +669,9 @@ function apply(ctx, config) {
664
669
  setSource: (source) => {
665
670
  current = source;
666
671
  },
667
- onChange: () => {},
672
+ onChange: () => {
673
+ ctx.emit("system-prompt/change");
674
+ },
668
675
  validate: (value) => {
669
676
  if (resolveConfig(value).providerId !== providerId) throw new Error("web-fetch-enhanced: providerId cannot be changed through live settings");
670
677
  createProvider(value);
@@ -683,6 +690,14 @@ function apply(ctx, config) {
683
690
  settingsCtx.effect(() => () => {
684
691
  if (isUnloading(ctx)) return;
685
692
  current = () => config;
693
+ ctx.emit("system-prompt/change");
694
+ });
695
+ });
696
+ ctx.inject(["systemPrompt"], (promptCtx) => {
697
+ promptCtx.systemPrompt.section({
698
+ name: "web-fetch-enhanced:allowlist",
699
+ order: 2105,
700
+ text: () => formatAllowlistPrompt(current())
686
701
  });
687
702
  });
688
703
  const dynamicProvider = {
@@ -720,4 +735,4 @@ function assertNonNegativeInteger(field, value) {
720
735
  if (!Number.isInteger(value) || value < 0) throw new Error(`web-fetch-enhanced: ${field} must be a non-negative integer`);
721
736
  }
722
737
  //#endregion
723
- export { Config, DEFAULT_PROVIDER_ID, DEFAULT_USER_AGENT, SETTINGS_NAMESPACE, apply, createProvider, defaultProxyRoute, inject, isNonPublicIpLiteral, name };
738
+ export { Config, DEFAULT_PROVIDER_ID, DEFAULT_USER_AGENT, SETTINGS_NAMESPACE, apply, createProvider, defaultProxyRoute, formatAllowlistPrompt, inject, isNonPublicIpLiteral, name };
@@ -1,4 +1,4 @@
1
- import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
2
2
  import type { LocaleKey } from './locales.ts';
3
3
  export interface AllowlistSettings {
4
4
  allowCidrs?: string[];
@@ -39,6 +39,23 @@ export interface Config {
39
39
  userAgent?: string;
40
40
  }
41
41
  export declare const Config: z<Config>;
42
+ interface SystemPromptSeam {
43
+ section(section: {
44
+ name: string;
45
+ order: number;
46
+ text: string | ((context?: unknown) => string);
47
+ }): () => void;
48
+ }
49
+ declare module '@deepseek-ai/cordis' {
50
+ interface Context {
51
+ systemPrompt?: SystemPromptSeam;
52
+ }
53
+ interface Events {
54
+ 'system-prompt/change'(): void;
55
+ }
56
+ }
57
+ /** Build prompt guidance copy informing the model of authorized non-public destinations. */
58
+ export declare function formatAllowlistPrompt(config: Config): string;
42
59
  /** Construct the provider without mounting it, useful for tests and custom compositions. */
43
60
  export declare function createProvider(config?: Config, proxyResolver?: ProxyRouteResolver): EnhancedHttpFetchProvider;
44
61
  export { isNonPublicIpLiteral } from './address-policy.ts';
@@ -33,10 +33,9 @@ export type ProxyRouteResult = {
33
33
  /** Function deciding whether a URL should be routed through an outbound proxy. */
34
34
  export type ProxyRouteResolver = (url: URL) => Promise<ProxyRouteResult> | ProxyRouteResult;
35
35
  /**
36
- * Default proxy route resolver: safely resolves proxy route via @deepseek-ai/dsh-http-proxy if available,
37
- * gracefully degrading to direct routing if the module is absent.
36
+ * Default proxy route resolver: resolves proxy route via @deepseek-ai/dsh-http-proxy.
38
37
  */
39
- export declare function defaultProxyRoute(url: URL): Promise<ProxyRouteResult>;
38
+ export declare function defaultProxyRoute(url: URL): ProxyRouteResult;
40
39
  /** Fetch while preserving the URL hostname for Host and TLS SNI. */
41
40
  export declare function requestPinned(url: URL, addresses: readonly ValidatedAddress[], headers: Record<string, string>, signal: AbortSignal): Promise<PinnedResponse>;
42
41
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-web-fetch-enhanced",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Configurable non-public address allowlists for DeepSeek Harness web_fetch",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -65,25 +65,20 @@
65
65
  "access": "public"
66
66
  },
67
67
  "engines": {
68
- "node": "^22.19.0 || >=24.0.0"
68
+ "node": "^22.19.0 || >=24.0.0",
69
+ "dsh": ">=0.1.5-rc.2"
69
70
  },
70
71
  "peerDependencies": {
71
72
  "@deepseek-ai/cordis": "^4.0.1 || ^4.0.2",
72
- "@deepseek-ai/dsh-timeout": "^0.1.2-rc.1",
73
- "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
74
- "@deepseek-ai/dsh-client-runtime": "^0.1.2-rc.1",
75
- "@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
76
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1",
77
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1",
78
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-rc.1",
79
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
80
- "@deepseek-ai/dsh-web": "^0.1.2-rc.1",
81
- "@deepseek-ai/dsh-http-proxy": "^0.1.2-rc.1"
82
- },
83
- "peerDependenciesMeta": {
84
- "@deepseek-ai/dsh-http-proxy": {
85
- "optional": true
86
- }
73
+ "@deepseek-ai/dsh-timeout": "^0.1.5-rc.2",
74
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
75
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
76
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
77
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
78
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2",
79
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
80
+ "@deepseek-ai/dsh-web": "^0.1.5-rc.2",
81
+ "@deepseek-ai/dsh-http-proxy": "^0.1.5-rc.2"
87
82
  },
88
83
  "dependencies": {
89
84
  "@deepseek-ai/schemastery": "^3.18.1 || ^3.18.2",
@@ -91,16 +86,16 @@
91
86
  "undici": "^8.10.0"
92
87
  },
93
88
  "devDependencies": {
94
- "@deepseek-ai/cordis": "^4.0.1",
95
- "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2",
96
- "@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
97
- "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
98
- "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
99
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.1-rc.2",
100
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
101
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.1-rc.2",
102
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.2",
103
- "@deepseek-ai/dsh-web": "^0.1.1-rc.2",
89
+ "@deepseek-ai/cordis": "^4.0.2",
90
+ "@deepseek-ai/dsh-timeout": "^0.1.5-rc.2",
91
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
92
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
93
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
94
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
95
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2",
96
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
97
+ "@deepseek-ai/dsh-web": "^0.1.5-rc.2",
98
+ "@deepseek-ai/dsh-http-proxy": "^0.1.5-rc.2",
104
99
  "@types/js-yaml": "^4.0.9",
105
100
  "@types/node": "^22.19.13",
106
101
  "@types/react": "~18.3.1",