@liangjiehappy/dsh-plugin-mfgabc 0.0.0-stage → 0.1.0

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,3 +1,41 @@
1
- # Temporary Holding Version
1
+ # dsh-plugin-mfgabc
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ mfgabc.com 只读查询插件:标准页、产业带城市、工艺集群、跨境合规、任意站内页。
4
+
5
+ ## 工具
6
+
7
+ | 工具 | 作用 |
8
+ |---|---|
9
+ | `mfgabc_standard` | 标准号 → 标准页(状态/标题/链接) |
10
+ | `mfgabc_city` | 城市产业带页 |
11
+ | `mfgabc_process` | 工艺集群页(工艺 + 城市) |
12
+ | `mfgabc_compliance` | 跨境合规页(品类 + 国家) |
13
+ | `mfgabc_page` | 任意站内路径查询 |
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ dsh plugin --profile web add @liangjiehappy/dsh-plugin-mfgabc
19
+ ```
20
+
21
+ ## 配置
22
+
23
+ ```yaml
24
+ - insert:
25
+ - id: dsh-plugin-mfgabc
26
+ name: dsh-plugin-mfgabc
27
+ config:
28
+ baseUrl: "https://www.mfgabc.com"
29
+ ```
30
+
31
+ ## 权限声明
32
+
33
+ - 只对 `www.mfgabc.com` 发 HTTPS GET
34
+ - 不读写文件、不读凭证、不执行 shell
35
+ - 只读:不提交表单、不登录、不写数据
36
+
37
+ ## 限制
38
+
39
+ - mfgabc 以静态页为主,工具返回页面状态/标题/链接,不做全文提取
40
+ - 标准页 slug 由标准号规范化生成;个别标准可能 404,工具会如实返回状态码
41
+ - 站点对 AI 训练爬虫有 403 策略;本插件以普通 UA 访问公开页面
@@ -0,0 +1,5 @@
1
+ - insert:
2
+ - id: dsh-plugin-mfgabc
3
+ name: "@liangjiehappy/dsh-plugin-mfgabc"
4
+ config:
5
+ baseUrl: "https://www.mfgabc.com"
package/lib/core.js ADDED
@@ -0,0 +1,57 @@
1
+ // dsh-plugin-mfgabc · 纯函数:站内路径构造与 HTML 解析
2
+ export const DEFAULT_BASE = 'https://www.mfgabc.com'
3
+
4
+ export function normalizeBase(base) {
5
+ const b = (base || DEFAULT_BASE).trim().replace(/\/+$/, '')
6
+ if (!/^https?:\/\//i.test(b)) throw new Error('baseUrl 必须以 http(s):// 开头')
7
+ return b
8
+ }
9
+
10
+ export function slugify(s) {
11
+ return String(s || '')
12
+ .trim()
13
+ .toLowerCase()
14
+ .replace(/[\/\s_.]+/g, '-')
15
+ .replace(/[^a-z0-9\u4e00-\u9fa5-]+/g, '')
16
+ .replace(/-+/g, '-')
17
+ .replace(/^-|-$/g, '')
18
+ }
19
+
20
+ /** 标准号 → mfgabc 标准页 slug:ISO 4703-1977 → ISO-4703-1977 */
21
+ export function normalizeStandard(no) {
22
+ return String(no || '')
23
+ .trim()
24
+ .toUpperCase()
25
+ .replace(/[\/\s_]+/g, '-')
26
+ .replace(/\./g, '-')
27
+ .replace(/-+/g, '-')
28
+ .replace(/^-|-$/g, '')
29
+ }
30
+
31
+ export function buildSiteUrl(base, path) {
32
+ if (!isSafePath(path)) throw new Error('非法站内路径')
33
+ return normalizeBase(base) + path
34
+ }
35
+
36
+ export function isSafePath(path) {
37
+ if (typeof path !== 'string' || !path.startsWith('/')) return false
38
+ if (path.includes('..') || path.includes('//')) return false
39
+ if (/[\s"'<>\\]/.test(path)) return false
40
+ return path.length <= 512
41
+ }
42
+
43
+ export function extractTitle(html) {
44
+ const m = String(html || '').match(/<title[^>]*>([\s\S]*?)<\/title>/i)
45
+ if (!m) return ''
46
+ return decodeEntities(m[1]).replace(/\s+/g, ' ').trim().slice(0, 200)
47
+ }
48
+
49
+ export function decodeEntities(s) {
50
+ return String(s)
51
+ .replace(/&amp;/g, '&')
52
+ .replace(/&lt;/g, '<')
53
+ .replace(/&gt;/g, '>')
54
+ .replace(/&quot;/g, '"')
55
+ .replace(/&#39;|&apos;/g, "'")
56
+ .replace(/&nbsp;/g, ' ')
57
+ }
package/lib/index.js ADDED
@@ -0,0 +1,100 @@
1
+ // dsh-plugin-mfgabc · mfgabc.com 只读页面查询
2
+ import { defineTool } from '@deepseek-ai/dsh-tools'
3
+ import {
4
+ DEFAULT_BASE, normalizeBase, normalizeStandard, slugify,
5
+ buildSiteUrl, isSafePath, extractTitle,
6
+ } from './core.js'
7
+
8
+ export const name = 'dsh-plugin-mfgabc'
9
+ export const inject = ['tools']
10
+
11
+ const text = (value) => [{ type: 'text', text: JSON.stringify(value, null, 2) }]
12
+ const headers = { 'user-agent': 'dsh-plugin-mfgabc/0.1', accept: 'text/html' }
13
+
14
+ export function apply(ctx, config = {}) {
15
+ const base = normalizeBase(config.baseUrl)
16
+
17
+ async function fetchPage(path, signal) {
18
+ const url = buildSiteUrl(base, path)
19
+ const r = await fetch(url, { headers, signal, redirect: 'follow' })
20
+ const html = await r.text()
21
+ return {
22
+ ok: r.ok,
23
+ status: r.status,
24
+ url: r.url || url,
25
+ title: extractTitle(html),
26
+ }
27
+ }
28
+
29
+ ctx.tools.register(defineTool({
30
+ name: 'mfgabc_standard',
31
+ description: '查 mfgabc.com 标准页:给标准号(如 ISO 4703-1977、GB/T 9114-2010),返回页面状态、标题与官方链接。',
32
+ parameters: {
33
+ number: { type: 'string', required: true, description: '标准号,如“ISO 4703-1977”' },
34
+ },
35
+ output: { schema: { type: 'json' }, render: (_a, v) => text(v) },
36
+ isConcurrencySafe: () => true,
37
+ async execute(args, exec) {
38
+ const slug = normalizeStandard(args.number)
39
+ return { slug, ...(await fetchPage(`/standards/s/${slug}/`, exec.signal)) }
40
+ },
41
+ }))
42
+
43
+ ctx.tools.register(defineTool({
44
+ name: 'mfgabc_city',
45
+ description: '查 mfgabc.com 产业带城市页(如 dongguan、shenzhen)。',
46
+ parameters: {
47
+ city: { type: 'string', required: true, description: '城市英文 slug,如 dongguan' },
48
+ },
49
+ output: { schema: { type: 'json' }, render: (_a, v) => text(v) },
50
+ isConcurrencySafe: () => true,
51
+ async execute(args, exec) {
52
+ const slug = slugify(args.city)
53
+ return { city: slug, ...(await fetchPage(`/cities/${slug}/`, exec.signal)) }
54
+ },
55
+ }))
56
+
57
+ ctx.tools.register(defineTool({
58
+ name: 'mfgabc_process',
59
+ description: '查 mfgabc.com 工艺集群页(城市 + 工艺,如 dongguan + stamping-die)。',
60
+ parameters: {
61
+ process: { type: 'string', required: true, description: '工艺 slug,如 stamping-die' },
62
+ city: { type: 'string', required: true, description: '城市 slug,如 dongguan' },
63
+ },
64
+ output: { schema: { type: 'json' }, render: (_a, v) => text(v) },
65
+ isConcurrencySafe: () => true,
66
+ async execute(args, exec) {
67
+ const p = slugify(args.process), c = slugify(args.city)
68
+ return { process: p, city: c, ...(await fetchPage(`/guide/clusters/${p}/${c}/`, exec.signal)) }
69
+ },
70
+ }))
71
+
72
+ ctx.tools.register(defineTool({
73
+ name: 'mfgabc_compliance',
74
+ description: '查 mfgabc.com 跨境合规页(品类 + 国家,如 plastic-products + singapore)。',
75
+ parameters: {
76
+ category: { type: 'string', required: true, description: '品类 slug,如 plastic-products' },
77
+ country: { type: 'string', required: true, description: '国家 slug,如 singapore' },
78
+ },
79
+ output: { schema: { type: 'json' }, render: (_a, v) => text(v) },
80
+ isConcurrencySafe: () => true,
81
+ async execute(args, exec) {
82
+ const cat = slugify(args.category), c = slugify(args.country)
83
+ return { category: cat, country: c, ...(await fetchPage(`/compliance/${cat}-${c}/`, exec.signal)) }
84
+ },
85
+ }))
86
+
87
+ ctx.tools.register(defineTool({
88
+ name: 'mfgabc_page',
89
+ description: '取 mfgabc.com 任意站内页面的状态、标题与最终链接(用于标准/材料/指南等已知路径)。',
90
+ parameters: {
91
+ path: { type: 'string', required: true, description: '站内路径,以 / 开头,如 /standards/' },
92
+ },
93
+ output: { schema: { type: 'json' }, render: (_a, v) => text(v) },
94
+ isConcurrencySafe: () => true,
95
+ async execute(args, exec) {
96
+ if (!isSafePath(args.path)) throw new Error('path 必须是站内绝对路径(以 / 开头,不含 .. 或 //)')
97
+ return fetchPage(args.path, exec.signal)
98
+ },
99
+ }))
100
+ }
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@liangjiehappy/dsh-plugin-mfgabc",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "mfgabc.com 制造业内容查询:标准页、产业带城市、工艺集群、跨境合规。只访问 www.mfgabc.com。",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "files": [
8
+ "lib",
9
+ "cordis.patch.yml",
10
+ "README.md"
11
+ ],
12
+ "dsh": {
13
+ "bundle": {
14
+ "patch": "./cordis.patch.yml"
15
+ }
16
+ },
17
+ "engines": {
18
+ "dsh": ">=0.2.0"
19
+ },
20
+ "peerDependencies": {
21
+ "@deepseek-ai/cordis": "*",
22
+ "@deepseek-ai/dsh-tools": "*"
23
+ },
24
+ "keywords": [
25
+ "deepseek-harness",
26
+ "dsh",
27
+ "dsh-plugin",
28
+ "mfgabc",
29
+ "manufacturing",
30
+ "standards",
31
+ "supply-chain"
32
+ ],
33
+ "license": "MIT",
34
+ "scripts": {
35
+ "test": "node --test"
36
+ }
37
+ }