dsh-cite 0.2.0 → 0.3.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 stardustlc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # dsh-cite
2
2
 
3
+ ![npm](https://img.shields.io/npm/v/dsh-cite) ![downloads](https://img.shields.io/npm/dm/dsh-cite) ![license](https://img.shields.io/github/license/STARDUSTLC666/dsh-cite) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-cite?style=social)
4
+
3
5
  > Give it a DOI, get a proper citation — GB/T 7714 / APA / MLA / Chicago / BibTeX.
4
6
 
5
- A DeepSeek Harness plugin for bibliographic references: query Crossref and format citations. Four tools, zero runtime dependencies.
7
+ A DeepSeek Harness plugin for bibliographic references: query Crossref and format citations. Five tools (including the `cite_health` self-check), zero runtime dependencies.
6
8
 
7
9
  ## Tools
8
10
 
@@ -12,6 +14,11 @@ A DeepSeek Harness plugin for bibliographic references: query Crossref and forma
12
14
  | `cite_format` | Generate a formatted citation | `doi`; `style`: gb-t-7714 / apa / mla / chicago |
13
15
  | `cite_bibtex` | Generate a BibTeX entry | `doi`; `key` optional |
14
16
  | `cite_check` | Extract DOIs from text and validate them | `text`; `maxChecks` 1-50, default 10 |
17
+ | `cite_health` | Self-check: probe Crossref connectivity and report latency | none |
18
+
19
+ ## Compatibility
20
+
21
+ Verified against `@deepseek-ai/dsh@0.1.2-alpha.2` on 2026-08-31. Built for the cordis patch-bundle plugin model (`cordis.patch.yml` + `dsh.bundle.patch`). No runtime imports of `@deepseek-ai/*` internals.
15
22
 
16
23
  ## Install
17
24
 
@@ -19,4 +26,14 @@ A DeepSeek Harness plugin for bibliographic references: query Crossref and forma
19
26
  dsh plugin --profile web add dsh-cite
20
27
  ```
21
28
 
22
- MIT
29
+ ## Uninstall
30
+
31
+ ```bash
32
+ dsh plugin --profile web remove dsh-cite
33
+ ```
34
+
35
+ Then restart the web service. To clean up fully, also remove the plugin entry from your profile `cordis.patch.yml` if you overrode it.
36
+
37
+ ## License
38
+
39
+ MIT (see [LICENSE](LICENSE))
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # dsh-cite
2
2
 
3
+ ![npm](https://img.shields.io/npm/v/dsh-cite) ![downloads](https://img.shields.io/npm/dm/dsh-cite) ![license](https://img.shields.io/github/license/STARDUSTLC666/dsh-cite) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-cite?style=social)
4
+
3
5
  > 给一个 DOI,还你规范参考文献——GB/T 7714 / APA / MLA / Chicago / BibTeX。
4
6
 
5
- DeepSeek Harness 参考文献工具插件:通过 Crossref API 查询文献元数据并格式化引用。四个工具、零运行时依赖、全平台通用。
7
+ DeepSeek Harness 参考文献工具插件:通过 Crossref API 查询文献元数据并格式化引用。五个工具(含 `cite_health` 自检)、零运行时依赖、全平台通用。
6
8
 
7
9
  ## 工具
8
10
 
@@ -12,6 +14,11 @@ DeepSeek Harness 参考文献工具插件:通过 Crossref API 查询文献元
12
14
  | `cite_format` | 生成规范引文 | `doi` 必填;`style`:gb-t-7714 / apa / mla / chicago |
13
15
  | `cite_bibtex` | 生成 BibTeX 条目 | `doi` 必填;`key` 可选 |
14
16
  | `cite_check` | 从文本提取 DOI 并并发校验是否存在(并发 3,保持输入顺序) | `text` 必填;`maxChecks` 1-50 默认 10 |
17
+ | `cite_health` | 自检:探测 Crossref 连通性并报告延迟 | 无 |
18
+
19
+ ## 兼容性
20
+
21
+ 在 `@deepseek-ai/dsh@0.1.2-alpha.2` 上验证(2026-08-31)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
15
22
 
16
23
  ## 安装
17
24
 
@@ -19,6 +26,15 @@ DeepSeek Harness 参考文献工具插件:通过 Crossref API 查询文献元
19
26
  dsh plugin --profile web add dsh-cite
20
27
  ```
21
28
 
29
+ ## 卸载
30
+
31
+ ```bash
32
+ dsh plugin --profile web remove dsh-cite
33
+ ```
34
+
35
+ 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
36
+
37
+
22
38
  ## 示例
23
39
 
24
40
  ```text
@@ -56,3 +72,6 @@ pnpm test # 构建 + 18 个测试
56
72
  ```
57
73
 
58
74
  MIT
75
+ ## License
76
+
77
+ MIT(见 [LICENSE](LICENSE))
package/lib/config.d.ts CHANGED
@@ -11,6 +11,7 @@ export interface ResolvedCiteConfig {
11
11
  timeoutMs: number;
12
12
  userAgent: string;
13
13
  }
14
+ export declare const DEFAULT_USER_AGENT = "dsh-cite/0.3.1 (DeepSeek Harness citation plugin; mailto:STARDUSTLC666@users.noreply.github.com)";
14
15
  export declare const CITE_TIMEOUT_ENV = "DSH_CITE_TIMEOUT_MS";
15
16
  export declare const CITE_USER_AGENT_ENV = "DSH_CITE_USER_AGENT";
16
17
  export declare function resolveConfig(config: CiteConfig | undefined | null, env?: NodeJS.ProcessEnv): ResolvedCiteConfig;
package/lib/config.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * @module dsh-cite/config
5
5
  */
6
6
  const DEFAULT_TIMEOUT_MS = 15000;
7
- const DEFAULT_USER_AGENT = 'dsh-cite/0.2.0 (DeepSeek Harness citation plugin; mailto:STARDUSTLC666@users.noreply.github.com)';
7
+ export const DEFAULT_USER_AGENT = 'dsh-cite/0.3.1 (DeepSeek Harness citation plugin; mailto:STARDUSTLC666@users.noreply.github.com)';
8
8
  export const CITE_TIMEOUT_ENV = 'DSH_CITE_TIMEOUT_MS';
9
9
  export const CITE_USER_AGENT_ENV = 'DSH_CITE_USER_AGENT';
10
10
  export function resolveConfig(config, env = process.env) {
package/lib/crossref.js CHANGED
@@ -6,7 +6,7 @@
6
6
  const CROSSREF_API = 'https://api.crossref.org/works/';
7
7
  const DOI_PATTERN = /^10\.\d{4,9}\/[-._;()/:A-Za-z0-9]+$/i;
8
8
  export function assertDoi(value) {
9
- const doi = value.trim().replace(/^https?:\/\/doi\.org\//i, '').replace(/^doi:\s*/i, '');
9
+ const doi = value.trim().replace(/^https?:\/\/(dx\.)?doi\.org\//i, '').replace(/^doi:\s*/i, '');
10
10
  if (!DOI_PATTERN.test(doi))
11
11
  throw new Error('DOI 格式不正确:' + value + '(示例:10.1038/nature12345)。');
12
12
  return doi;
@@ -135,7 +135,7 @@ export function extractDois(text) {
135
135
  const seen = new Set();
136
136
  const result = [];
137
137
  for (const match of text.match(pattern) ?? []) {
138
- const clean = match.replace(/[.,;]+$/, '').trim();
138
+ const clean = match.replace(/[.,;:)\]}'">]+$/, '').trim();
139
139
  if (seen.has(clean.toLowerCase()))
140
140
  continue;
141
141
  seen.add(clean.toLowerCase());
package/lib/format.js CHANGED
@@ -26,7 +26,7 @@ function initials(given) {
26
26
  }
27
27
  function apaName(author) {
28
28
  const family = author.family !== '' ? author.family : (author.name !== '' ? author.name : author.given);
29
- const initialsText = author.given !== '' ? initials(author.given) : (author.name !== '' ? initials(author.name.split(/[\s-]+/)[0] ?? '') : '');
29
+ const initialsText = author.given !== '' ? initials(author.given) : '';
30
30
  return initialsText !== '' ? family + ', ' + initialsText : family;
31
31
  }
32
32
  function mlaName(author, first) {
@@ -52,7 +52,9 @@ function apaAuthors(authors) {
52
52
  return names[0];
53
53
  if (names.length === 2)
54
54
  return names[0] + ', & ' + names[1];
55
- return names.slice(0, 19).join(', ') + ', ...';
55
+ if (names.length <= 20)
56
+ return names.slice(0, -1).join(', ') + ', & ' + names[names.length - 1];
57
+ return names.slice(0, 19).join(', ') + ', ... ' + names[names.length - 1];
56
58
  }
57
59
  function mlaAuthors(authors) {
58
60
  if (authors.length === 0)
@@ -144,14 +146,15 @@ function buildApa(work) {
144
146
  text += '(' + work.issue + ')';
145
147
  if (work.page !== '')
146
148
  text += ', ' + work.page;
147
- text += '. ';
148
- text += work.doi !== '' ? 'https://doi.org/' + work.doi : work.url;
149
- return text;
149
+ const tail = work.doi !== '' ? 'https://doi.org/' + work.doi : work.url;
150
+ if (tail !== '')
151
+ return text + '. ' + tail;
152
+ return text + '.';
150
153
  }
151
154
  if (work.type === 'book' || work.type === 'monograph' || work.type === 'edited-book') {
152
- return authors + ' (' + year + '). ' + title + '. ' + (work.publisher !== '' ? work.publisher + '.' : '');
155
+ return authors + ' (' + year + '). ' + title + '.' + (work.publisher !== '' ? ' ' + work.publisher + '.' : '');
153
156
  }
154
- return authors + ' (' + year + '). ' + title + '. ' + (work.url !== '' ? work.url : '');
157
+ return authors + ' (' + year + '). ' + title + '.' + (work.url !== '' ? ' ' + work.url : '');
155
158
  }
156
159
  /** 生成 MLA 9 参考文献条目。 */
157
160
  function buildMla(work) {
@@ -201,7 +204,21 @@ export function buildCitation(work, style, lang = 'zh') {
201
204
  }
202
205
  }
203
206
  function escapeLatex(value) {
204
- return value.replace(/([{}])/g, '\\$1').replace(/&/g, '\\&').replace(/%/g, '\\%').replace(/\$/g, '\\$');
207
+ return value.replace(/[\\{}&%$#_~^]/g, (ch) => {
208
+ switch (ch) {
209
+ case '\\': return '\\textbackslash{}';
210
+ case '{': return '\\{';
211
+ case '}': return '\\}';
212
+ case '&': return '\\&';
213
+ case '%': return '\\%';
214
+ case '$': return '\\$';
215
+ case '#': return '\\#';
216
+ case '_': return '\\_';
217
+ case '~': return '\\textasciitilde{}';
218
+ case '^': return '\\textasciicircum{}';
219
+ default: return ch;
220
+ }
221
+ });
205
222
  }
206
223
  /** BibTeX 引用键只允许安全字符,空格/逗号/花括号等替换为下划线。 */
207
224
  function sanitizeBibtexKey(value) {
@@ -225,7 +242,7 @@ function bibtexType(work) {
225
242
  export function buildBibtex(work, key) {
226
243
  const first = work.authors[0];
227
244
  const firstWord = titleOf(work).split(/[^\p{L}\p{N}]+/u).find((part) => part !== '') ?? 'work';
228
- const baseKey = key !== undefined && key.trim() !== '' ? sanitizeBibtexKey(key.trim()) : ((first?.family ?? first?.name ?? 'author') + yearText(work) + firstWord).toLowerCase();
245
+ const baseKey = key !== undefined && key.trim() !== '' ? sanitizeBibtexKey(key.trim()) : sanitizeBibtexKey(((first?.family || first?.name || 'author') + yearText(work) + firstWord).toLowerCase());
229
246
  const type = bibtexType(work);
230
247
  const fields = [];
231
248
  const authors = work.authors.map((author) => fullName(author)).join(' and ');
package/lib/index.d.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * dsh-cite —— 参考文献工具插件(node 半身,配置走 cordis.patch.yml)。
3
3
  *
4
- * 插件导出 apply(ctx, config):注册四个面向模型的工具(cite_lookup / cite_format /
5
- * cite_bibtex / cite_check),通过 Crossref API 查询元数据并生成 GB/T 7714 / APA /
6
- * MLA / Chicago 引文。零运行时依赖。
4
+ * 插件导出 apply(ctx, config):注册五个面向模型的工具(cite_lookup / cite_format /
5
+ * cite_bibtex / cite_check / cite_health),通过 Crossref API 查询元数据并生成
6
+ * GB/T 7714 / APA / MLA / Chicago 引文。零运行时依赖。
7
7
  *
8
8
  * @module dsh-cite
9
9
  */
10
10
  import { type CiteConfig } from './config.js';
11
11
  import { type CiteToolDefinition } from './tools.js';
12
12
  /** cordis 服务注入:apply 里要用 ctx.tools。 */
13
+ export declare const name = "cite";
13
14
  export declare const inject: string[];
14
15
  export interface CitePluginContext {
15
16
  tools: {
package/lib/index.js CHANGED
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * dsh-cite —— 参考文献工具插件(node 半身,配置走 cordis.patch.yml)。
3
3
  *
4
- * 插件导出 apply(ctx, config):注册四个面向模型的工具(cite_lookup / cite_format /
5
- * cite_bibtex / cite_check),通过 Crossref API 查询元数据并生成 GB/T 7714 / APA /
6
- * MLA / Chicago 引文。零运行时依赖。
4
+ * 插件导出 apply(ctx, config):注册五个面向模型的工具(cite_lookup / cite_format /
5
+ * cite_bibtex / cite_check / cite_health),通过 Crossref API 查询元数据并生成
6
+ * GB/T 7714 / APA / MLA / Chicago 引文。零运行时依赖。
7
7
  *
8
8
  * @module dsh-cite
9
9
  */
10
10
  import { resolveConfig } from './config.js';
11
11
  import { buildCiteTools } from './tools.js';
12
12
  /** cordis 服务注入:apply 里要用 ctx.tools。 */
13
+ export const name = 'cite';
13
14
  export const inject = ['tools'];
14
15
  export function apply(ctx, config) {
15
16
  let cfg;
package/lib/tools.js CHANGED
@@ -207,5 +207,49 @@ export function buildCiteTools(cfg, fetchImpl) {
207
207
  },
208
208
  timeoutMs: cfg.timeoutMs * 3 + 5000,
209
209
  };
210
- return [citeLookup, citeFormat, citeBibtex, citeCheck];
210
+ const citeHealth = {
211
+ name: 'cite_health',
212
+ description: 'dsh-cite 自检:向 Crossref API 发一个最小请求验证连通性(rows=1),报告延迟。遇到问题时先运行本工具定位。',
213
+ parameters: compileParameters({}),
214
+ output: {
215
+ schema: { type: 'object', additionalProperties: true },
216
+ render: (_args, value) => {
217
+ const rec = (value ?? {});
218
+ const rawChecks = Array.isArray(rec.checks) ? rec.checks : [];
219
+ const checks = rawChecks.map((item) => (item ?? {}));
220
+ const lines = ['dsh-cite 自检' + (rec.ok === true ? ':正常。' : ':发现问题。')];
221
+ for (const c of checks) {
222
+ lines.push('- ' + c.name + ':' + (c.ok === true ? '✅' : '❌ ' + String(c.detail ?? '')));
223
+ }
224
+ return [{ type: 'text', text: lines.join('\n') }];
225
+ },
226
+ },
227
+ async execute() {
228
+ const checks = [];
229
+ let ok = true;
230
+ const startedAt = Date.now();
231
+ try {
232
+ const fetcher = fetchImpl ?? globalThis.fetch;
233
+ const response = await fetcher('https://api.crossref.org/works?rows=1&select=DOI', {
234
+ headers: { 'user-agent': cfg.userAgent },
235
+ signal: AbortSignal.timeout(Math.min(cfg.timeoutMs, 15000)),
236
+ });
237
+ if (response.ok) {
238
+ checks.push({ name: 'Crossref API', ok: true, detail: 'HTTP ' + response.status + ',' + (Date.now() - startedAt) + 'ms' });
239
+ }
240
+ else {
241
+ ok = false;
242
+ checks.push({ name: 'Crossref API', ok: false, detail: 'HTTP ' + response.status });
243
+ }
244
+ }
245
+ catch (error) {
246
+ ok = false;
247
+ checks.push({ name: 'Crossref API', ok: false, detail: (error instanceof Error ? error.message : String(error)) + '(网络需要特殊代理时请配置系统代理后重启)' });
248
+ }
249
+ checks.push({ name: '请求配置', ok: true, detail: 'timeoutMs=' + cfg.timeoutMs + ',UA 已配置' });
250
+ return { ok, plugin: 'dsh-cite', checks };
251
+ },
252
+ timeoutMs: Math.min(cfg.timeoutMs + 5000, 30000),
253
+ };
254
+ return [citeLookup, citeFormat, citeBibtex, citeCheck, citeHealth];
211
255
  }
package/package.json CHANGED
@@ -1,64 +1,63 @@
1
- {
2
- "name": "dsh-cite",
3
- "version": "0.2.0",
4
- "description": "DSH 参考文献工具插件:cite_lookup/cite_format/cite_bibtex/cite_check 四工具,Crossref 元数据查询(含 DOI 精确解析与并发校验),GB/T 7714 / APA / MLA / Chicago 格式化,零运行时依赖。",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/index.d.ts",
11
- "default": "./lib/index.js"
12
- },
13
- "./cordis.patch.yml": "./cordis.patch.yml",
14
- "./package.json": "./package.json"
15
- },
16
- "files": [
17
- "lib",
18
- "cordis.patch.yml",
19
- "README.md",
20
- "README.en.md"
21
- ],
22
- "scripts": {
23
- "build": "tsc -p tsconfig.json",
24
- "prepare": "tsc -p tsconfig.json",
25
- "typecheck": "tsc -p tsconfig.json --noEmit",
26
- "test": "pnpm run build && node --test \"test/*.test.mjs\"",
27
- "prepublishOnly": "pnpm run build"
28
- },
29
- "dsh": {
30
- "bundle": {
31
- "patch": "./cordis.patch.yml"
32
- }
33
- },
34
- "keywords": [
35
- "dsh",
36
- "deepseek-harness",
37
- "plugin",
38
- "citation",
39
- "crossref",
40
- "gb-t-7714",
41
- "apa",
42
- "mla",
43
- "chicago",
44
- "dsh-plugin"
45
- ],
46
- "license": "MIT",
47
- "engines": {
48
- "node": ">=22.13"
49
- },
50
- "devDependencies": {
51
- "@types/node": "^24.0.0",
52
- "typescript": "^5.6.0"
53
- },
54
- "repository": {
55
- "type": "git",
56
- "url": "git+https://github.com/STARDUSTLC666/dsh-cite.git"
57
- },
58
- "bugs": {
59
- "url": "https://github.com/STARDUSTLC666/dsh-cite/issues"
60
- },
61
- "homepage": "https://github.com/STARDUSTLC666/dsh-cite#readme",
62
- "author": "stardustlc",
63
- "packageManager": "pnpm@11.7.0"
64
- }
1
+ {
2
+ "name": "dsh-cite",
3
+ "version": "0.3.1",
4
+ "description": "DSH 参考文献工具插件:cite_lookup/cite_format/cite_bibtex/cite_check/cite_health 五工具(含自检),Crossref 元数据查询(含 DOI 精确解析与并发校验),GB/T 7714 / APA / MLA / Chicago 格式化,零运行时依赖。",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./cordis.patch.yml": "./cordis.patch.yml",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "lib",
18
+ "cordis.patch.yml",
19
+ "README.md",
20
+ "README.en.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "test": "pnpm run build && node --test \"test/*.test.mjs\"",
26
+ "prepublishOnly": "pnpm run build"
27
+ },
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ }
32
+ },
33
+ "keywords": [
34
+ "dsh",
35
+ "deepseek-harness",
36
+ "plugin",
37
+ "citation",
38
+ "crossref",
39
+ "gb-t-7714",
40
+ "apa",
41
+ "mla",
42
+ "chicago",
43
+ "dsh-plugin"
44
+ ],
45
+ "license": "MIT",
46
+ "engines": {
47
+ "node": ">=22"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^24.0.0",
51
+ "typescript": "^5.6.0"
52
+ },
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/STARDUSTLC666/dsh-cite.git"
56
+ },
57
+ "bugs": {
58
+ "url": "https://github.com/STARDUSTLC666/dsh-cite/issues"
59
+ },
60
+ "homepage": "https://github.com/STARDUSTLC666/dsh-cite#readme",
61
+ "author": "stardustlc",
62
+ "packageManager": "pnpm@11.7.0"
63
+ }