dsh-rss 0.4.1 → 0.4.3

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
@@ -1,10 +1,10 @@
1
1
  # dsh-rss
2
2
 
3
- ## 0.4.1 update (2026-09-27)
3
+ ## 0.4.3 update (2026-10-01)
4
4
 
5
- Explains feed resolution failures caused by proxy Fake-IP DNS and suggests using a real DNS address while keeping reserved-address protection enabled.
5
+ Fixes URL comparison for add, remove, OPML import and search. Schemes, hosts and default ports are normalized while case-sensitive paths and query parameters remain distinct, preventing unrelated subscriptions from being merged or removed. Existing Fake-IP DNS guidance and reserved-address checks remain available.
6
6
 
7
- Validation host: Harness 0.1.7-rc.2 built from official sources, retaining the local tool-scheduler fix. Build and automated checks pass; interactive coverage and external-service limits are recorded in this release round.
7
+ Validation host: Harness `0.2.0-rc.2` built from official sources (commit `639ed01539`) on 2026-10-01. All 81 Windows tests, the 18-plugin co-load and nine RSS tool contracts pass. Production external feeds still require separate network validation.
8
8
 
9
9
  ![npm](https://img.shields.io/npm/v/dsh-rss) ![downloads](https://img.shields.io/npm/dm/dsh-rss) ![license](https://img.shields.io/github/license/STARDUSTLC666/dsh-rss) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-rss?style=social)
10
10
 
@@ -14,7 +14,7 @@ DSH (DeepSeek Harness) plugin for RSS/Atom subscriptions: manage feeds, fetch an
14
14
 
15
15
  ## Compatibility
16
16
 
17
- Version 0.4.0 supports official-source Harness **0.1.7-alpha.2** (2026-09-23, with a local `Symbol.for` tool-scheduler fix). All 18 plugins load together; tool contracts, subscription updates and restart persistence pass in isolation. Live feeds require working network access. Requires Node 22.19 or later within 22.x, or 24 or later.
17
+ Version 0.4.0 supports the declarative settings interface introduced in Harness **0.1.7**. The current baseline is official-source **0.2.0-rc.2**. All 18 plugins load together; tool contracts, subscription updates and restart persistence pass in isolation. Live feeds require working network access. Requires Node 22.19 or later within 22.x, or 24 or later.
18
18
 
19
19
  Fetches honor the Harness cancellation signal through DNS, response streaming, and cross-feed searches. Hostname preflight checks reject loopback, private, and link-local addresses by default, including redirect destinations. Set `allowPrivateNetwork: true` for trusted internal feeds.
20
20
 
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  # dsh-rss
4
4
 
5
- ## 0.4.1 更新(2026-09-27)
5
+ ## 0.4.3 更新(2026-10-01)
6
6
 
7
- 识别代理 Fake-IP 造成的订阅源解析问题,提示使用真实 DNS 地址;继续拒绝保留地址,无需放开内网访问。
7
+ 修复订阅 URL 的大小写比较:添加、删除、OPML 导入与搜索统一归一化协议、域名和默认端口,保留路径和查询参数的大小写,避免合并或删除不同订阅源。保留此前的代理 Fake-IP DNS 提示和保留地址检查。
8
8
 
9
- 验证宿主:官方源码构建的 Harness 0.1.7-rc.2(保留本地工具调度器修复)。构建与自动测试通过;实际操作和外部服务限制见本轮验收记录。
9
+ 验证宿主:官方源码构建的 Harness `0.2.0-rc.2`(commit `639ed01539`,2026-10-01)。81 项 Windows 插件测试、18 个插件共同加载与 9 个 RSS 工具的契约检查通过。真实外部订阅源仍需正常网络连接。
10
10
 
11
11
  > **agent 的资讯雷达**:订阅管理 + RSS/Atom 抓取解析。
12
12
 
@@ -19,7 +19,7 @@ DSH(DeepSeek Harness)的 RSS/Atom 订阅工具插件:管理订阅源,抓
19
19
 
20
20
  ## 兼容性
21
21
 
22
- 0.4.0 适配官方源码构建的 Harness **0.1.7-alpha.2**(2026-09-23,含本地工具调度器 `Symbol.for` 修复)。18 个插件同载、工具契约、订阅读写与重启持久化通过隔离验证;真实外部订阅源需要正常网络连接。Node 要求为 22.19 及以上的 22.x,或 24 及以上。
22
+ 0.4.0 起适配官方源码构建的 Harness **0.1.7 及以上的声明式设置接口**;当前基线为 **0.2.0-rc.2**,18 个插件同载、工具契约、订阅读写与重启持久化在隔离环境通过验证;真实外部订阅源需要正常网络连接。Node 要求为 22.19 及以上的 22.x,或 24 及以上。
23
23
 
24
24
  抓取接收 Harness 的取消信号,取消 DNS、网络读取或跨源搜索时保留原始取消原因。默认预检域名解析结果并拒绝回环、私网和链路本地地址;每次重定向都重新校验。可信内网源可显式配置 `allowPrivateNetwork: true`。
25
25
 
package/lib/feeds.d.ts CHANGED
@@ -10,8 +10,10 @@ export interface Feed {
10
10
  export declare function parseFeedsYaml(text: string): Feed[];
11
11
  /** 订阅数组序列化回 YAML 文本(带使用说明头)。 */
12
12
  export declare function serializeFeeds(feeds: Feed[]): string;
13
+ /** Normalize URL scheme, host and default port while preserving path/query case. */
14
+ export declare function sameFeedUrl(a: string, b: string): boolean;
13
15
  /**
14
- * 添加订阅:按 url(不区分大小写)去重;已存在时合并更新 name/category。
16
+ * 添加订阅:按 URL 去重,保留路径与查询值的大小写;已存在时合并更新 name/category。
15
17
  */
16
18
  export declare function addFeed(feeds: Feed[], url: string, name: string, category: string): {
17
19
  feeds: Feed[];
@@ -19,7 +21,7 @@ export declare function addFeed(feeds: Feed[], url: string, name: string, catego
19
21
  existed: boolean;
20
22
  };
21
23
  /**
22
- * 删除订阅:url 精确匹配(不区分大小写),或 name 匹配所有同名项(不区分大小写)。
24
+ * 删除订阅:URL 路径与查询值精确匹配,或 name 匹配所有同名项(不区分大小写)。
23
25
  * 两者都缺或没有匹配项时抛中文错误。
24
26
  */
25
27
  export declare function removeFeed(feeds: Feed[], url?: string, name?: string): {
package/lib/feeds.js CHANGED
@@ -65,17 +65,23 @@ export function serializeFeeds(feeds) {
65
65
  const body = YAML.stringify(rows).trimEnd();
66
66
  return FEEDS_HEADER.join('\n') + body + '\n';
67
67
  }
68
- function sameUrl(a, b) {
69
- return a.trim().toLowerCase() === b.trim().toLowerCase();
68
+ /** Normalize URL scheme, host and default port while preserving path/query case. */
69
+ export function sameFeedUrl(a, b) {
70
+ try {
71
+ return new URL(a.trim()).href === new URL(b.trim()).href;
72
+ }
73
+ catch {
74
+ return a.trim() === b.trim();
75
+ }
70
76
  }
71
77
  /**
72
- * 添加订阅:按 url(不区分大小写)去重;已存在时合并更新 name/category。
78
+ * 添加订阅:按 URL 去重,保留路径与查询值的大小写;已存在时合并更新 name/category。
73
79
  */
74
80
  export function addFeed(feeds, url, name, category) {
75
81
  const cleanUrl = url.trim();
76
82
  const cleanName = name.trim();
77
83
  const cleanCategory = category.trim();
78
- const existing = feeds.find((feed) => sameUrl(feed.url, cleanUrl));
84
+ const existing = feeds.find((feed) => sameFeedUrl(feed.url, cleanUrl));
79
85
  if (existing) {
80
86
  const merged = {
81
87
  url: existing.url,
@@ -89,7 +95,7 @@ export function addFeed(feeds, url, name, category) {
89
95
  return { feeds: [...feeds, added], added, existed: false };
90
96
  }
91
97
  /**
92
- * 删除订阅:url 精确匹配(不区分大小写),或 name 匹配所有同名项(不区分大小写)。
98
+ * 删除订阅:URL 路径与查询值精确匹配,或 name 匹配所有同名项(不区分大小写)。
93
99
  * 两者都缺或没有匹配项时抛中文错误。
94
100
  */
95
101
  export function removeFeed(feeds, url, name) {
@@ -100,7 +106,7 @@ export function removeFeed(feeds, url, name) {
100
106
  }
101
107
  const removed = [];
102
108
  const next = feeds.filter((feed) => {
103
- const match = (cleanUrl !== '' && sameUrl(feed.url, cleanUrl)) || (cleanName !== '' && feed.name.toLowerCase() === cleanName.toLowerCase());
109
+ const match = (cleanUrl !== '' && sameFeedUrl(feed.url, cleanUrl)) || (cleanName !== '' && feed.name.toLowerCase() === cleanName.toLowerCase());
104
110
  if (match)
105
111
  removed.push(feed);
106
112
  return !match;
package/lib/tools.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * @module dsh-rss/tools
6
6
  */
7
7
  import { writeFile } from 'node:fs/promises';
8
- import { addFeed, findFeedsByName, parseFeedsYaml, removeFeed, serializeFeeds } from './feeds.js';
8
+ import { addFeed, findFeedsByName, parseFeedsYaml, removeFeed, sameFeedUrl, serializeFeeds } from './feeds.js';
9
9
  import { buildOpml, importOpmlFeeds, parseOpml } from './opml.js';
10
10
  import { parseFeed } from './parser.js';
11
11
  import { assertHttpUrl, fetchFeedXml } from './network.js';
@@ -461,7 +461,7 @@ export function buildRssTools(config, settingsScope, fetchImpl, lookupImpl) {
461
461
  };
462
462
  const rssRemove = {
463
463
  name: 'rss_remove',
464
- description: '删除已订阅的 RSS/Atom 源。url 与 name 至少给一个:url 精确匹配(不区分大小写),name 匹配所有同名订阅。删除结果持久化到 settings。',
464
+ description: '删除已订阅的 RSS/Atom 源。url 与 name 至少给一个:url 的协议、主机大小写与默认端口可归一化,路径和查询值精确匹配;name 匹配所有同名订阅。删除结果持久化到 settings。',
465
465
  parameters: compileParameters({
466
466
  url: { type: 'string', description: '要删除的订阅源地址(可选,与 name 至少给一个)。' },
467
467
  name: { type: 'string', description: '要删除的订阅显示名(可选,与 url 至少给一个;同名全部删除)。' },
@@ -641,7 +641,7 @@ export function buildRssTools(config, settingsScope, fetchImpl, lookupImpl) {
641
641
  const nameFilter = optionalString(args, 'name');
642
642
  let targets = getFeeds();
643
643
  if (urlFilter !== undefined)
644
- targets = targets.filter((feed) => feed.url.toLowerCase() === urlFilter.toLowerCase());
644
+ targets = targets.filter((feed) => sameFeedUrl(feed.url, urlFilter));
645
645
  if (nameFilter !== undefined)
646
646
  targets = findFeedsByName(targets, nameFilter);
647
647
  if (targets.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-rss",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "DSH RSS 插件:订阅源管理、OPML 导入导出、抓取解析 RSS/Atom 并保留正文。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",