wensu 0.4.0__tar.gz
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.
- wensu-0.4.0/.gitignore +42 -0
- wensu-0.4.0/AGENTS.md +37 -0
- wensu-0.4.0/CHANGELOG.md +470 -0
- wensu-0.4.0/LICENSE +21 -0
- wensu-0.4.0/NOTICE +89 -0
- wensu-0.4.0/PKG-INFO +314 -0
- wensu-0.4.0/README.md +262 -0
- wensu-0.4.0/docs/acceptance.md +108 -0
- wensu-0.4.0/docs/adr/0001-microkernel.md +22 -0
- wensu-0.4.0/docs/adr/0002-read-only.md +19 -0
- wensu-0.4.0/docs/adr/0003-native-bridge.md +21 -0
- wensu-0.4.0/docs/adr/0004-risk-stop.md +26 -0
- wensu-0.4.0/docs/adr/0005-quota-300.md +44 -0
- wensu-0.4.0/docs/adr/0006-no-third-party-ingest.md +38 -0
- wensu-0.4.0/docs/architecture.md +211 -0
- wensu-0.4.0/docs/bridge-protocol.md +109 -0
- wensu-0.4.0/docs/browser-setup.md +155 -0
- wensu-0.4.0/docs/channel-contract.md +73 -0
- wensu-0.4.0/docs/cnki-maintenance.md +120 -0
- wensu-0.4.0/docs/incidents.md +98 -0
- wensu-0.4.0/docs/integration/codex.md +169 -0
- wensu-0.4.0/docs/integration/dsh.md +47 -0
- wensu-0.4.0/docs/integration/zotero.md +99 -0
- wensu-0.4.0/docs/operations.md +174 -0
- wensu-0.4.0/docs/publishing.md +153 -0
- wensu-0.4.0/docs/reference-parity.md +54 -0
- wensu-0.4.0/extension/background.js +186 -0
- wensu-0.4.0/extension/content/actions.js +281 -0
- wensu-0.4.0/extension/manifest.json +34 -0
- wensu-0.4.0/native-host/README.md +46 -0
- wensu-0.4.0/native-host/install.ps1 +97 -0
- wensu-0.4.0/native-host/launcher.cs +91 -0
- wensu-0.4.0/native-host/main.py +249 -0
- wensu-0.4.0/pyproject.toml +131 -0
- wensu-0.4.0/schemas/record.json +28 -0
- wensu-0.4.0/schemas/tools.json +58 -0
- wensu-0.4.0/skills/scholar-detail/SKILL.md +54 -0
- wensu-0.4.0/skills/scholar-export/SKILL.md +49 -0
- wensu-0.4.0/skills/scholar-install/SKILL.md +160 -0
- wensu-0.4.0/skills/scholar-search/SKILL.md +117 -0
- wensu-0.4.0/src/scholar_search/__init__.py +6 -0
- wensu-0.4.0/src/scholar_search/adapters/__init__.py +1 -0
- wensu-0.4.0/src/scholar_search/adapters/bridge/__init__.py +150 -0
- wensu-0.4.0/src/scholar_search/adapters/bridge/host.py +187 -0
- wensu-0.4.0/src/scholar_search/adapters/bridge/policy.py +83 -0
- wensu-0.4.0/src/scholar_search/adapters/cdp/__init__.py +1 -0
- wensu-0.4.0/src/scholar_search/adapters/fixture/__init__.py +68 -0
- wensu-0.4.0/src/scholar_search/assets.py +42 -0
- wensu-0.4.0/src/scholar_search/bootstrap.py +51 -0
- wensu-0.4.0/src/scholar_search/channels/__init__.py +1 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/__init__.py +1 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/manifest.json +20 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/__init__.py +98 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/doc_types.py +69 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/parse_detail.py +169 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/parse_search.py +96 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/plan_detail.py +22 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/plan_search.py +180 -0
- wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/selectors.py +53 -0
- wensu-0.4.0/src/scholar_search/cli.py +97 -0
- wensu-0.4.0/src/scholar_search/core/__init__.py +1 -0
- wensu-0.4.0/src/scholar_search/core/audit.py +70 -0
- wensu-0.4.0/src/scholar_search/core/cache.py +72 -0
- wensu-0.4.0/src/scholar_search/core/config.py +100 -0
- wensu-0.4.0/src/scholar_search/core/contracts.py +302 -0
- wensu-0.4.0/src/scholar_search/core/errors.py +85 -0
- wensu-0.4.0/src/scholar_search/core/export.py +217 -0
- wensu-0.4.0/src/scholar_search/core/locking.py +89 -0
- wensu-0.4.0/src/scholar_search/core/params.py +84 -0
- wensu-0.4.0/src/scholar_search/core/planner.py +86 -0
- wensu-0.4.0/src/scholar_search/core/policy.py +229 -0
- wensu-0.4.0/src/scholar_search/core/registry.py +135 -0
- wensu-0.4.0/src/scholar_search/core/risk.py +145 -0
- wensu-0.4.0/src/scholar_search/core/service.py +230 -0
- wensu-0.4.0/src/scholar_search/server.py +87 -0
- wensu-0.4.0/src/scholar_search/transports/__init__.py +1 -0
- wensu-0.4.0/src/scholar_search/transports/http.py +87 -0
- wensu-0.4.0/src/scholar_search/transports/stdio.py +13 -0
- wensu-0.4.0/tests/fixtures/cnki_detail_2026_10.html +15 -0
- wensu-0.4.0/tests/fixtures/cnki_detail_online_metadata_2026_10.html +13 -0
- wensu-0.4.0/tests/fixtures/cnki_detail_print_2026_10.html +19 -0
- wensu-0.4.0/tests/fixtures/cnki_search_2026_10.html +4 -0
- wensu-0.4.0/tests/fixtures/cnki_search_mixed_types.html +33 -0
wensu-0.4.0/.gitignore
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
|
|
10
|
+
# Native host build outputs and per-user registration
|
|
11
|
+
native-host/scholar-host.exe
|
|
12
|
+
native-host/python-path.txt
|
|
13
|
+
native-host/com.scholar.search.json
|
|
14
|
+
native-host/run-host.cmd
|
|
15
|
+
|
|
16
|
+
# Tooling caches
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
.pre-commit-cache/
|
|
23
|
+
|
|
24
|
+
# Local runtime data (must never be committed)
|
|
25
|
+
data/
|
|
26
|
+
fixtures/local/
|
|
27
|
+
*.local.json
|
|
28
|
+
*.local.toml
|
|
29
|
+
.env
|
|
30
|
+
.env.*
|
|
31
|
+
|
|
32
|
+
# Logs
|
|
33
|
+
*.log
|
|
34
|
+
|
|
35
|
+
# OS / editor
|
|
36
|
+
.DS_Store
|
|
37
|
+
Thumbs.db
|
|
38
|
+
.idea/
|
|
39
|
+
.vscode/
|
|
40
|
+
|
|
41
|
+
# Local handoff notes (machine-specific paths; not for public history)
|
|
42
|
+
HANDOFF.md
|
wensu-0.4.0/AGENTS.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# AGENTS.md · 项目硬约束
|
|
2
|
+
|
|
3
|
+
本文件是给所有在此仓库工作的 agent 的硬性约束。与代码注释冲突时,以本文件为准。
|
|
4
|
+
|
|
5
|
+
## 不可协商
|
|
6
|
+
|
|
7
|
+
1. **只读**:不实现、不调用任何 PDF/CAJ 下载接口。允许 RIS/EndNote 题录文本导出。
|
|
8
|
+
2. **不做第三方题录入库**:不实现把题录写入 Zotero、EndNote、Mendeley 等参考文献库的任何接口、
|
|
9
|
+
桥接或自动化(包括本地 HTTP 端点、库文件改写、数据库直写)。只输出纯文本 RIS/EndNote,
|
|
10
|
+
导入由用户自己在对应软件里完成。理由见 ADR 0006:自动入库会越过本站的服务条款边界,
|
|
11
|
+
并把第三方软件的账号与库文件卷进本项目的责任范围。
|
|
12
|
+
3. **遇风控即停**:检测到验证码、`/verify/home`、403、登录重定向,立即终止当前计划,返回 `blocked`,写审计,冷却 24 小时。禁止自动重试、禁止更换 IP、禁止延时后重试。
|
|
13
|
+
4. **不破解验证码**:任何形式的自动识别、打码、绕过都不允许。
|
|
14
|
+
5. **频率硬下限**:最小间隔 3000ms、并发 1、单次 ≤3 页、每日 ≤300 次(ADR 0005)。这些值原则上只能收紧;放宽必须新增 ADR。
|
|
15
|
+
6. **动作词汇封闭**:`goto` `wait_for` `type` `click` `select` `read_html` `read_text` `scroll` `sleep`。新增动词需 ADR 并同步 host 与扩展。
|
|
16
|
+
7. **域名白名单**:由渠道 manifest 声明,内核与 host 双重校验。
|
|
17
|
+
|
|
18
|
+
## 开发规范
|
|
19
|
+
|
|
20
|
+
- Python 3.11+,类型全标注,`mypy --strict` 通过。
|
|
21
|
+
- `ruff` 格式化与检查,提交前跑 `pre-commit`。
|
|
22
|
+
- Conventional Commits:`feat:` `fix:` `docs:` `refactor:` `test:` `chore:`。
|
|
23
|
+
- 新增渠道 = 新增 `channels/<name>/<site_version>/` + `manifest.json`,不改内核。
|
|
24
|
+
- 解析逻辑必须有 fixture 回放测试,禁止只靠线上验证。
|
|
25
|
+
- 运行期数据只写 `SCHOLAR_SEARCH_HOME`(默认 `%LOCALAPPDATA%\scholar-search`),不得写入仓库。
|
|
26
|
+
|
|
27
|
+
## 隐私
|
|
28
|
+
|
|
29
|
+
- 不记录 Cookie、账号、完整 HTML。
|
|
30
|
+
- 审计日志只记动作、渠道、耗时、结果数、错误码、风控信号。
|
|
31
|
+
- fixture 必须脱敏;仓库零凭证。
|
|
32
|
+
|
|
33
|
+
## 发布
|
|
34
|
+
|
|
35
|
+
- `main` 分支保护,tag 触发发布。
|
|
36
|
+
- 版本号、CHANGELOG、NOTICE 三者必须同步。
|
|
37
|
+
|
wensu-0.4.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
Versioning: [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.4.0] - 2026-10-23
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Distribution renamed from `scholar-search` to `wensu` (文溯). The old name is taken on PyPI
|
|
12
|
+
by an unrelated package, so `pip install scholar-search` would install someone else's code.
|
|
13
|
+
- The command is now `wensu`; `scholar-search` remains as a compatibility alias, so MCP client
|
|
14
|
+
configs written before this release keep working.
|
|
15
|
+
- The Python module (`scholar_search`), the runtime directory
|
|
16
|
+
(`%LOCALAPPDATA%\scholar-search`) and `SCHOLAR_SEARCH_HOME` are unchanged, so upgrades keep
|
|
17
|
+
quota, cooldown and audit state.
|
|
18
|
+
- Repository and documentation links point at `github.com/hui-xu-ai/wensu`.
|
|
19
|
+
|
|
20
|
+
## [0.3.19] - 2026-10-21
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- `NOTICE` now carries a full provenance statement: which public MIT projects informed this
|
|
25
|
+
one, exactly what was taken (selector strings, step order, known pitfalls) and what was
|
|
26
|
+
not (every file, module and function here is written for this project), plus the
|
|
27
|
+
licence of each runtime and development dependency.
|
|
28
|
+
- `README` gained a 来源与致谢 section; `docs/reference-parity.md` no longer calls the
|
|
29
|
+
selectors "照搬" and points at the provenance statement.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Test fixtures are now fully desensitised: recorded abstracts, author names,
|
|
34
|
+
affiliations and keyword labels are replaced with synthetic placeholders, and author
|
|
35
|
+
mail addresses are removed. Only markup, class names and public bibliographic facts
|
|
36
|
+
(title, journal, volume/issue/pages, DOI) remain.
|
|
37
|
+
|
|
38
|
+
### Docs
|
|
39
|
+
|
|
40
|
+
- Version numbers in examples are placeholders now (`<版本>`) instead of a pinned release,
|
|
41
|
+
so they cannot go stale; only the status section names a real version.
|
|
42
|
+
- The proxy trap (TLS interception making CNKI show a certificate error) is documented in
|
|
43
|
+
the README, the Codex guide, the install skill and the incident ledger, including the
|
|
44
|
+
detail that a command-line fetch bypasses the system proxy and therefore proves nothing.
|
|
45
|
+
|
|
46
|
+
## [0.3.18] - 2026-10-21
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- `core/locking.py` branches on `sys.platform` instead of `os.name`. mypy only narrows
|
|
51
|
+
the former, so on Linux the Windows branch was analysed too: `msvcrt` came back
|
|
52
|
+
untyped and the `fcntl` ignores were reported as unused. That made the
|
|
53
|
+
`ubuntu-latest` jobs fail at the type-check step while Windows passed. Verified with
|
|
54
|
+
both `mypy src` and `mypy --platform linux src`.
|
|
55
|
+
|
|
56
|
+
## [0.3.17] - 2026-10-21
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- The sort-correction plan's pause now uses the shared reply margin. It still carried the
|
|
61
|
+
old `sleep 1500 / timeout 2000` pair that 0.3.15 proved too tight for a throttled tab;
|
|
62
|
+
the plan only runs when the grid comes back ascending, so it had not failed yet.
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- `settle()` in `channels/cnki/v2026_10/plan_search.py` is public so the sort-correction
|
|
67
|
+
plan reuses it instead of hand-rolling its own pause budget.
|
|
68
|
+
|
|
69
|
+
### Docs
|
|
70
|
+
|
|
71
|
+
- User guide, architecture, upgrade/operations, CNKI-maintenance, browser setup, Zotero
|
|
72
|
+
(manual import only) and publishing notes; plus a `scholar-install` skill that walks an
|
|
73
|
+
agent through a fresh install.
|
|
74
|
+
|
|
75
|
+
## [0.3.16] - 2026-10-21
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
|
|
79
|
+
- Document types are normalised in one place. The result grid labels some rows
|
|
80
|
+
「中文图书」/「外文期刊」, and the old fallback (`token.casefold()`) pushed that raw
|
|
81
|
+
Chinese string into `doc_type`, where the export mapping could not recognise it and
|
|
82
|
+
RIS fell back to `GEN` — a book exported as generic. `parse_search` and
|
|
83
|
+
`parse_detail` now share `channels/cnki/v2026_10/doc_types.py`, which strips language
|
|
84
|
+
prefixes, wrappers (《期刊》, (网络首发)) and the 文献类型 label, and returns
|
|
85
|
+
`None` for a type it cannot map so the export renders generic with a note instead of a
|
|
86
|
+
meaningless value.
|
|
87
|
+
|
|
88
|
+
## [0.3.15] - 2026-10-21
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
|
|
92
|
+
- Pause deadlines now carry a 2 s reply margin. In live use the 2.5 s settle after a
|
|
93
|
+
navigation answered at 3004 ms against a 3000 ms deadline and the plan died with
|
|
94
|
+
`bridge.timeout`. The cause is timer alignment: while the CNKI window is not the
|
|
95
|
+
active window the browser buckets timers to whole seconds, so any pause can answer up
|
|
96
|
+
to ~1 s late. Every `sleep` action now uses `_settle()`, which adds the margin.
|
|
97
|
+
- Worst-case plan budget rebalanced to stay inside the 120 s cap that the extra margins
|
|
98
|
+
would have broken: `read_html` 6 s → 4 s, grid/filter waits 8 s → 6 s, query-box and
|
|
99
|
+
result-grid waits 12 s → 10 s. Observed live latency is unchanged (navigation ~1.9 s,
|
|
100
|
+
grid ~2.1 s), so each deadline still sits 3–5× above it. ADR 0005 limits, the verb
|
|
101
|
+
set and the plan shape are untouched.
|
|
102
|
+
|
|
103
|
+
## [0.3.14] - 2026-10-21
|
|
104
|
+
|
|
105
|
+
### Fixed
|
|
106
|
+
|
|
107
|
+
- Search and expert-search plans wait 2.5 s after the opening navigation before looking
|
|
108
|
+
for the query box. CNKI's first load performs a second hop that replaced the content
|
|
109
|
+
script, so `type` could fail with `bridge.timeout` even though `goto` and `wait_for`
|
|
110
|
+
had succeeded. Behaviour, limits and verbs are unchanged; the plan now has one extra
|
|
111
|
+
`sleep` action.
|
|
112
|
+
|
|
113
|
+
## [0.3.13] - 2026-10-21
|
|
114
|
+
|
|
115
|
+
### Changed
|
|
116
|
+
|
|
117
|
+
- Daily network-action ceiling raised from 200 to 300 per ADR 0005. All other limits
|
|
118
|
+
are unchanged: 3 s floor plus jitter, concurrency 1, page 1–3, `limit` 1–50,
|
|
119
|
+
40 actions / 120 s per plan, 24 h cooldown.
|
|
120
|
+
- Documented captcha handling: the plan stops and returns `blocked`; the user solves
|
|
121
|
+
the challenge in the browser, then explicitly clears the cooldown with
|
|
122
|
+
`scholar-search reset --confirm`. No automatic retry.
|
|
123
|
+
|
|
124
|
+
## [0.3.12] - 2026-10-21
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
|
|
128
|
+
- Returned `page` now reflects the page that was actually requested, even when the
|
|
129
|
+
site's own pager marker lags behind the AJAX turn.
|
|
130
|
+
- The 50-per-page switch waits 2.5 s for the grid to rebuild; a fast read returned the
|
|
131
|
+
previous 20-row page.
|
|
132
|
+
- CNKI dissertation DOIs (`/d.cnki.`) mark a detail record as `thesis` when no explicit
|
|
133
|
+
type field is present; live verification now exports `THES`/`Thesis`.
|
|
134
|
+
|
|
135
|
+
## [0.3.11] - 2026-10-21
|
|
136
|
+
|
|
137
|
+
### Changed
|
|
138
|
+
|
|
139
|
+
- **Human-like pacing** The kernel now adds 0–1.2 s of random jitter on top of the
|
|
140
|
+
3 s floor, with a ~10% chance of a 2–4 s pause. Clicks scroll the target into view and
|
|
141
|
+
fire mousedown/mouseup/click with random 40–90 ms gaps; navigation gets a 0.5–1.5 s
|
|
142
|
+
dwell before the first action. Typing stays paste-like, as a person would paste a query.
|
|
143
|
+
- **Sort correction** The grid is read after sorting; if the active column is `ASC`, the
|
|
144
|
+
channel returns one corrective plan and the service runs it, instead of blind
|
|
145
|
+
double-clicks. This keeps the requested descending order without relying on timing.
|
|
146
|
+
|
|
147
|
+
## [0.3.10] - 2026-10-21
|
|
148
|
+
|
|
149
|
+
### Fixed
|
|
150
|
+
|
|
151
|
+
- **Date sort direction** The CNKI grid already opens on publication-time DESC;
|
|
152
|
+
the product clicked the tab once, which toggled it to ASC. `sort=date` now sends no
|
|
153
|
+
sort clicks. Citation sort clicks twice (first activation ASC, second DESC).
|
|
154
|
+
- **Pagination settle** A first page turn now waits 800 ms after the grid renders; the
|
|
155
|
+
turn was previously clicked while the grid was still binding and was ignored.
|
|
156
|
+
- **Content-script readiness** The background retries `sendMessage` for up to ~1.8 s
|
|
157
|
+
when a navigation outruns `document_idle` injection, instead of failing with
|
|
158
|
+
"Receiving end does not exist".
|
|
159
|
+
- Read timeout trimmed to 7 s so every query/page/sort/page-size combination stays
|
|
160
|
+
within the 120 s plan budget.
|
|
161
|
+
|
|
162
|
+
## [0.3.9] - 2026-10-21
|
|
163
|
+
|
|
164
|
+
### Fixed
|
|
165
|
+
|
|
166
|
+
- **Click delivery** 0.3.8 deferred the click with setTimeout, which Chrome throttles
|
|
167
|
+
in background tabs; pagination then executed after the read and stayed on page 1.
|
|
168
|
+
The content script clicks synchronously again, and the background now treats a
|
|
169
|
+
click whose reply is lost to navigation as started (the plan's wait_for verifies
|
|
170
|
+
the page). Non-click verbs still require a real reply.
|
|
171
|
+
|
|
172
|
+
## [0.3.8] - 2026-10-21
|
|
173
|
+
|
|
174
|
+
### Fixed
|
|
175
|
+
|
|
176
|
+
- **Click reply loss** A submit/search click can start a navigation that destroys the
|
|
177
|
+
content-script context before its reply is sent, producing `bridge.timeout` at the
|
|
178
|
+
kernel. The content script now replies first and activates the element on the next
|
|
179
|
+
task; the plan still verifies the result with its following `wait_for`.
|
|
180
|
+
|
|
181
|
+
## [0.3.7] - 2026-10-21
|
|
182
|
+
|
|
183
|
+
### Fixed
|
|
184
|
+
|
|
185
|
+
- **Live detail type** Online-first journal detail pages have no volume/issue, so the
|
|
186
|
+
previous metadata-only rule left `doc_type` empty and exported `GEN`. The journal
|
|
187
|
+
header link text (`查看该刊数据库收录来源`) is now used as article-level structural
|
|
188
|
+
evidence, so real online-first details export `JOUR`. Verified live on a CNKI
|
|
189
|
+
journal article (abstract, DOI, keywords, 0 risk).
|
|
190
|
+
|
|
191
|
+
## [0.3.6] - 2026-10-21
|
|
192
|
+
|
|
193
|
+
### Fixed
|
|
194
|
+
|
|
195
|
+
- Read detail document types from complete type values or explicitly labelled
|
|
196
|
+
type fields. Funding, subjects and source names cannot supply a document type;
|
|
197
|
+
explicit online-first journal metadata is retained.
|
|
198
|
+
- Reserve two seconds between CNKI search content waits and their outer action
|
|
199
|
+
deadlines, allowing a timeout response and its risk snapshot to reach the kernel.
|
|
200
|
+
- Add synthetic online-first metadata fixtures and regression coverage for all
|
|
201
|
+
supported query/page/sort/page-size combinations. Live verification is pending.
|
|
202
|
+
- Accept wrapped (`《期刊》`) and status-suffixed (`期刊(网络首发)`) type values, and
|
|
203
|
+
scan `.top-first` with the same exact-value rule, so explicit types are not lost
|
|
204
|
+
to page decoration. Body/recommendation text still cannot supply a type.
|
|
205
|
+
|
|
206
|
+
## [0.3.5] - 2026-10-21
|
|
207
|
+
|
|
208
|
+
### Fixed
|
|
209
|
+
|
|
210
|
+
- **R034-01** Detail document type is read only from the article metadata header
|
|
211
|
+
(`.top-first`, `.doc-top`, `.top-tip`, `li.top-space`). Body text, abstracts and
|
|
212
|
+
related recommendations can no longer re-type the current article, and a concrete
|
|
213
|
+
volume/issue/pages citation always wins as journal evidence. Fixtures added for
|
|
214
|
+
"related thesis recommendation" and "conference notice in body".
|
|
215
|
+
|
|
216
|
+
## [0.3.4] - 2026-10-21
|
|
217
|
+
|
|
218
|
+
### Fixed
|
|
219
|
+
|
|
220
|
+
- **CSP** Clicks on CNKI's `<a href="javascript:void(0);">` pagination/sort links are
|
|
221
|
+
cancelled at the default-action stage, so the page CSP no longer blocks a
|
|
222
|
+
`javascript:` navigation while the site's own click handlers still run. Plain
|
|
223
|
+
links and inputs are clicked as before.
|
|
224
|
+
|
|
225
|
+
## [0.3.3] - 2026-10-21
|
|
226
|
+
|
|
227
|
+
### Fixed
|
|
228
|
+
|
|
229
|
+
- **F01** The result table's `td.data` document type is parsed into `Record.doc_type`;
|
|
230
|
+
RIS/EndNote now map journal/thesis/conference/newspaper/book/patent/standard/yearbook,
|
|
231
|
+
and an unknown or missing type renders as `GEN` / `Generic` (plus a RIS note) instead of
|
|
232
|
+
being silently exported as a journal article. `schemas/record.json` and the export
|
|
233
|
+
validator include the new field; mixed-type fixture added.
|
|
234
|
+
- **F02** Detail readiness now waits for the article-level selector
|
|
235
|
+
`.wx-tit h1, #ChDivSummary` instead of a bare `h1` (the login modal), and the inner wait
|
|
236
|
+
is 8s so it finishes inside the 15s action budget and can still return the final page
|
|
237
|
+
or risk snapshot.
|
|
238
|
+
|
|
239
|
+
## [0.3.2] - 2026-10-21
|
|
240
|
+
|
|
241
|
+
### Fixed
|
|
242
|
+
|
|
243
|
+
- **V01** A quota reservation that cannot be persisted now blocks that same action;
|
|
244
|
+
an existing but corrupt/unreadable quota file is a hard error instead of being read
|
|
245
|
+
as zero usage.
|
|
246
|
+
- **V02** If the cross-process plan lock cannot be created, the plan is refused; the
|
|
247
|
+
process-local fallback was removed.
|
|
248
|
+
- **V03** The shared cooldown is re-checked after the interval sleep and immediately
|
|
249
|
+
before the browser action.
|
|
250
|
+
- **V04** A cross-domain login redirect is still refused, but the reply keeps the final
|
|
251
|
+
URL and risk metadata so the kernel can persist the 24h cooldown.
|
|
252
|
+
- **V05** Extension permissions and content injection are scoped to the enabled
|
|
253
|
+
channel (`https://*.cnki.net/*`) and top frame only.
|
|
254
|
+
|
|
255
|
+
### Changed
|
|
256
|
+
|
|
257
|
+
- Skills now match the real contract: one page per call, no fabricated export
|
|
258
|
+
`count`, and a single, consistent rule against batch detail loops.
|
|
259
|
+
- `install.ps1` falls back to `python`/`python3` on PATH and records the runtime HOME
|
|
260
|
+
in `runtime-home.txt`; the host reads it at startup.
|
|
261
|
+
- Launcher logs move under `SCHOLAR_SEARCH_HOME/host/logs`.
|
|
262
|
+
- `scholar-search assets` refuses to overwrite existing directories without `--force`.
|
|
263
|
+
- Added `docs/integration/codex.md`.
|
|
264
|
+
|
|
265
|
+
## [0.3.1] - 2026-10-21
|
|
266
|
+
|
|
267
|
+
Risk follow-up fixes from `independent-20261021-02-risk-followup`.
|
|
268
|
+
|
|
269
|
+
### Fixed
|
|
270
|
+
|
|
271
|
+
- **R01** Every content-script reply (success, timeout, missing element, exception)
|
|
272
|
+
carries the risk snapshot; `click`/`type`/`select` refuse to run while a captcha or
|
|
273
|
+
login gate is already visible; the background no longer drops `data` on failures.
|
|
274
|
+
- **R02** A whole plan holds a cross-process lock; the quota slot is reserved
|
|
275
|
+
atomically in `wait_turn` (interval check + increment + persist), so two processes
|
|
276
|
+
cannot both spend the last request.
|
|
277
|
+
- **R03** The shared cooldown is re-checked before every action, not only at plan start.
|
|
278
|
+
- **R04** Actions stay bound to the tab opened by the plan's `goto`; a plan cannot
|
|
279
|
+
drift to whatever tab the user activates. The extension keeps a configured host
|
|
280
|
+
scope, and the host only lets a request narrow its trusted startup scope, never
|
|
281
|
+
replace it.
|
|
282
|
+
- **R05** If quota persistence fails, the next `wait_turn` fails closed instead of
|
|
283
|
+
trusting the stale on-disk counter.
|
|
284
|
+
- **R06** `HTTP 429` (status or error text) is now a terminal `rate_limited` signal.
|
|
285
|
+
|
|
286
|
+
### Compatibility
|
|
287
|
+
|
|
288
|
+
Two assertions in the earlier audit conflict with the newer risk scope and now fail
|
|
289
|
+
by design: the host no longer accepts an arbitrary `example.org` manifest without a
|
|
290
|
+
trusted scope, and the extension no longer navigates to unconfigured hosts.
|
|
291
|
+
|
|
292
|
+
## [0.3.0] - 2026-10-21
|
|
293
|
+
|
|
294
|
+
Security and contract hardening from the independent offline audit
|
|
295
|
+
(`independent-20261021-01`). Breaking: several invalid inputs that were quietly
|
|
296
|
+
accepted now fail loudly.
|
|
297
|
+
|
|
298
|
+
### Fixed
|
|
299
|
+
|
|
300
|
+
- **F01** Navigation replies carry the final URL and current title; the adapter
|
|
301
|
+
surfaces a nested `data.url`; HTTP status is collected from the content script
|
|
302
|
+
and `403` reaches the risk guard.
|
|
303
|
+
- **F02** Captcha visibility requires an on-screen, non-zero-size element that
|
|
304
|
+
intersects the viewport; every action result carries the risk snapshot.
|
|
305
|
+
- **F03** Quota and interval are read and written under an inter-process lock;
|
|
306
|
+
plans are serialized process-wide, so two servers cannot double-spend.
|
|
307
|
+
- **F04** The adapter never replays an action after a connection failure.
|
|
308
|
+
- **F05** A failed action stops the plan; the service raises `ActionError` and
|
|
309
|
+
never parses or caches the page as a success.
|
|
310
|
+
- **F06** The cooldown deadline is latched in memory, so a failed state write
|
|
311
|
+
cannot unblock the process that just detected a risk.
|
|
312
|
+
- **F07** Cache hits still respect an active cooldown.
|
|
313
|
+
- **F08** Replies are correlated with request ids; a handshake precedes the
|
|
314
|
+
first action; the bridge protocol doc separates TCP JSON from native framing.
|
|
315
|
+
- **F09** One closed parameter schema (`core/params.py`) is enforced by kernel
|
|
316
|
+
and host; deadlines above 15s are rejected; frames are capped at 2 MiB on
|
|
317
|
+
write and `read_html` truncates by UTF-8 bytes with a `truncated` flag;
|
|
318
|
+
`wait_for` honours `state=hidden`.
|
|
319
|
+
- **F10** Export validates records against the public schema and refuses
|
|
320
|
+
newline/type injection in any field.
|
|
321
|
+
- **F11** Malformed search pages and non-article detail pages raise `ParseError`
|
|
322
|
+
instead of being cached as empty/valid results.
|
|
323
|
+
- **F12** Detail URL authorisation uses the parsed path prefix.
|
|
324
|
+
- **F13** Old site-version directories stay discoverable; selecting a version
|
|
325
|
+
no longer requires every directory to match the channel manifest.
|
|
326
|
+
- **F14** Domain authority moves to the selected manifest (sent per request);
|
|
327
|
+
the host no longer hard-codes CNKI and the extension accepts any https host.
|
|
328
|
+
- **F15** Host runtime files live under `SCHOLAR_SEARCH_HOME/host`; the
|
|
329
|
+
installer labels that directory low-integrity writable.
|
|
330
|
+
- **F16** HTTP Host check compares the whole hostname, not a prefix.
|
|
331
|
+
- **F17** Wheel ships native-host, extension and schemas as package assets;
|
|
332
|
+
`scholar-search assets --target DIR` extracts them; sdist includes browser
|
|
333
|
+
sources and fixtures.
|
|
334
|
+
- **F18** `schemas/tools.json` matches the four live tools; README, NOTICE and
|
|
335
|
+
package metadata are on 0.3.0.
|
|
336
|
+
- **F19** `status.bridge_ready` probes a live host instead of checking for a file.
|
|
337
|
+
- **F20** `ruff format --check` passes and `.pre-commit-config.yaml` is present.
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
## [0.2.6] - 2026-10-21
|
|
341
|
+
|
|
342
|
+
### Added
|
|
343
|
+
|
|
344
|
+
- `sort=date` and `sort=citation` drive the result grid's sort tabs (`#orderList`).
|
|
345
|
+
A relevance reset precedes the target click, because the grid remembers the previous
|
|
346
|
+
direction and a bare click would toggle unpredictably.
|
|
347
|
+
- Live-verified: `limit=50, sort=date` returns 50 rows in strict descending date order
|
|
348
|
+
(2026 → 2024), 12 pages, no verification prompt.
|
|
349
|
+
|
|
350
|
+
## [0.2.5] - 2026-10-21
|
|
351
|
+
|
|
352
|
+
### Added
|
|
353
|
+
|
|
354
|
+
- Field detection for expert search now covers every documented code: `SU`, `TKA`, `KY`,
|
|
355
|
+
`TI`, `FT`, `AU`, `FI`, `RP`, `AF`, `FU`, `AB`, `CO`, `RF`, `CLC`, `LY`, `DOI`, `CF`.
|
|
356
|
+
Unknown `XX=` / `XX%` codes also route to the expert page instead of the one-box search.
|
|
357
|
+
- Skill documentation now lists the official field table and operators (`and`/`*`,
|
|
358
|
+
`or`/`+`, `not`/`-`, `%` for author matching).
|
|
359
|
+
- Live-verified: `TI='IPMC' and AU % '王'` returns 43 hits across 2014–2026.
|
|
360
|
+
|
|
361
|
+
## [0.2.4] - 2026-10-21
|
|
362
|
+
|
|
363
|
+
### Added
|
|
364
|
+
|
|
365
|
+
- `limit > 20` switches the result grid to 50 rows per page (12 pages for the 595-hit
|
|
366
|
+
`IPMC` query); with the 3-page cap a single call returns up to ~150 records.
|
|
367
|
+
- `year_from` / `year_to` are applied client-side to the fetched records. The site's own
|
|
368
|
+
year control is never touched; `total` still reports the site's full result count.
|
|
369
|
+
|
|
370
|
+
### Fixed
|
|
371
|
+
|
|
372
|
+
- Search URLs carry a per-call nonce so every call starts from the grid's default state;
|
|
373
|
+
previously a repeat navigation could inherit the previous page size and break the switch.
|
|
374
|
+
|
|
375
|
+
## [0.2.3] - 2026-10-21
|
|
376
|
+
|
|
377
|
+
### Fixed
|
|
378
|
+
|
|
379
|
+
- Account-gate detection now requires a **visible** login modal; the previous body-text match
|
|
380
|
+
flagged CNKI's always-present login panel and produced a false cooldown.
|
|
381
|
+
- Native hosts register themselves under `hosts/` and answer a ping; the adapter picks a live
|
|
382
|
+
host instead of trusting a single stale `bridge.json`.
|
|
383
|
+
- Removed host “retirement”: it caused reconnect churn where each new host killed the previous one.
|
|
384
|
+
- Verified after the fix: one read-only search returned 595 hits, no cooldown.
|
|
385
|
+
|
|
386
|
+
## [0.2.2] - 2026-10-21
|
|
387
|
+
|
|
388
|
+
### Changed
|
|
389
|
+
|
|
390
|
+
- Year filters are disabled: the result-page year control (`#btnFilterYear`) triggered an
|
|
391
|
+
account verification gate (“手机号认证”) during a live test. `year_from` / `year_to`
|
|
392
|
+
now fail with an explicit error instead of touching that control.
|
|
393
|
+
- Risk guard detects account verification gates via the content script (`auth_required`)
|
|
394
|
+
and reports `login_redirect`, so a gate cannot be mistaken for an empty result page.
|
|
395
|
+
- Incident recorded in `docs/incidents.md`; 62 tests.
|
|
396
|
+
|
|
397
|
+
## [0.2.1] - 2026-10-21
|
|
398
|
+
|
|
399
|
+
### Fixed
|
|
400
|
+
|
|
401
|
+
- Detail parser reads volume/issue/pages from the `.top-first` citation line
|
|
402
|
+
(`力学学报 . 2022 ,54 (01) : 184-195`); verified by fixture replay and a live MCP call.
|
|
403
|
+
- Added `tests/fixtures/cnki_detail_print_2026_10.html` and a regression test (59 tests).
|
|
404
|
+
|
|
405
|
+
## [0.2.0] - 2026-10-20
|
|
406
|
+
|
|
407
|
+
### Added
|
|
408
|
+
|
|
409
|
+
- Kernel pipeline: registry, policy, risk guard, planner, audit, cache, service.
|
|
410
|
+
- Channel registry with site-version selection and manifest/version consistency checks.
|
|
411
|
+
- Policy engine: verb/domain allowlists, 3s hard floor, daily quota, plan size and duration limits.
|
|
412
|
+
- Risk guard: captcha/verify/403/login detection, 24h persisted cooldown, manual reset only.
|
|
413
|
+
- JSONL audit log (structural facts only, no page content or credentials).
|
|
414
|
+
- SQLite response cache with TTL.
|
|
415
|
+
- RIS and EndNote text export.
|
|
416
|
+
- Fixture adapter for offline replay.
|
|
417
|
+
- 40 unit and contract tests covering policy, risk, planner, cache, registry, service, export.
|
|
418
|
+
- Native bridge: 4-byte-length JSON framing, request correlation, timeout handling.
|
|
419
|
+
- Bridge policy: nine-verb allowlist, https-only, CNKI host allowlist, sleep bound.
|
|
420
|
+
- Loopback TCP channel between host and MCP server, per-run random token.
|
|
421
|
+
- MV3 extension skeleton (background relay + nine generic DOM verbs).
|
|
422
|
+
- `native-host/install.ps1` registers the host for Edge and Chrome under HKCU.
|
|
423
|
+
- 47 tests including bridge framing, policy and TCP round-trip.
|
|
424
|
+
- CNKI channel v2026.10: search plan, result-table parser, detail plan and parser.
|
|
425
|
+
- MCP server with four tools: search, detail, export, status.
|
|
426
|
+
- stdio transport and loopback HTTP transport with bearer token and Host check.
|
|
427
|
+
- `scholar-search status` reports channels, cooldown, quota and bridge readiness.
|
|
428
|
+
- 51 tests; ruff and mypy --strict clean.
|
|
429
|
+
- Verified selectors against a live CNKI page (595 hits, 20 rows); trimmed fixture in `tests/fixtures`.
|
|
430
|
+
- Search plan now drives the search box (goto → type → click → wait → read) instead of a static URL.
|
|
431
|
+
- Native host launcher is a compiled .exe: Edge refuses to start `.cmd` hosts.
|
|
432
|
+
- Host runtime files (endpoint, log) moved to the per-user temp directory, because browsers launch native hosts at low integrity and `AppData\Local` is not writable there.
|
|
433
|
+
- Single-instance check and per-connection replies fix duplicate-host races.
|
|
434
|
+
- 52 tests.
|
|
435
|
+
- Expert search: field expressions (`SU=`, `TI=`, `KY=`, `AU=` …) go through `AdvSearch?type=expert`.
|
|
436
|
+
- Pagination: `page=2..3` clicks `#PageNext` and waits for the next result batch.
|
|
437
|
+
- Search limit is honoured (`limit` was previously ignored).
|
|
438
|
+
- Cache keys include the package version so parser changes do not serve stale hits.
|
|
439
|
+
- Plan budget stays under 120s via per-action timeouts; page capped at 3.
|
|
440
|
+
- 56 tests; live-verified: keyword 595 hits, expert expression 57 hits, page 2 distinct.
|
|
441
|
+
- Detail parser rewritten for the 2026 layout: `.wx-tit h1`, `h3.author`, `li.top-space` rows; strips author affiliation marks and “附视频” suffixes.
|
|
442
|
+
- Risk guard now flags verification interstitials by document title (“安全验证”), after an expired detail URL served one.
|
|
443
|
+
- Native host runs one process per browser connection: the previous single-instance check deadlocked reconnects.
|
|
444
|
+
- Skills: `scholar-search`, `scholar-detail`, `scholar-export`.
|
|
445
|
+
- Docs: reference parity table and incident log.
|
|
446
|
+
- 58 tests; release workflow on version tags.
|
|
447
|
+
|
|
448
|
+
## [0.1.0] - 2026-10-20
|
|
449
|
+
|
|
450
|
+
### Added
|
|
451
|
+
|
|
452
|
+
- Project skeleton: microkernel layout, channel plugin directory, adapters, transports.
|
|
453
|
+
- Core contracts (`core/contracts.py`): action vocabulary, plan, record, channel and adapter protocols.
|
|
454
|
+
- Hard limits in `core/config.py` (3s floor, 1 concurrency, 3 pages per call, 200 per day, 24h cooldown).
|
|
455
|
+
- Error hierarchy with risk-blocked payload.
|
|
456
|
+
- ADRs 0001-0004: microkernel, read-only, native bridge, risk stop.
|
|
457
|
+
- JSON schemas for records and MCP tools.
|
|
458
|
+
- CNKI channel manifest (site version 2026.10), implementation pending.
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
wensu-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 scholar-meta contributors
|
|
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.
|
wensu-0.4.0/NOTICE
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
wensu (formerly scholar-search)
|
|
2
|
+
===============================
|
|
3
|
+
|
|
4
|
+
Version: 0.4.0 (2026-10-23)
|
|
5
|
+
|
|
6
|
+
Academic search: a read-only, microkernel-based scholarly metadata service.
|
|
7
|
+
|
|
8
|
+
This project is an independent, non-official tool. It is not affiliated with,
|
|
9
|
+
endorsed by, or sponsored by any academic database operator.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Provenance and third-party notices
|
|
13
|
+
----------------------------------
|
|
14
|
+
|
|
15
|
+
This codebase was written for this project. No source files, modules or functions
|
|
16
|
+
were copied from another repository. What it does reuse is narrower than "code":
|
|
17
|
+
|
|
18
|
+
1. CSS selector strings that identify elements on CNKI pages. These are facts
|
|
19
|
+
about a public web page (class names, ids), not creative expression; they
|
|
20
|
+
were checked against live pages and are covered by fixture tests.
|
|
21
|
+
2. The order of the interaction steps (open search page, fill box, submit,
|
|
22
|
+
read grid), which is dictated by how the site works.
|
|
23
|
+
3. Knowledge of known pitfalls, such as the hidden captcha widget that stays in
|
|
24
|
+
the DOM, which would otherwise produce false positives.
|
|
25
|
+
|
|
26
|
+
Those were informed by three public MIT-licensed projects:
|
|
27
|
+
|
|
28
|
+
cookjohn/cnki-skills https://github.com/cookjohn/cnki-skills
|
|
29
|
+
SepineTam/cnki-mcp https://github.com/SepineTam/cnki-mcp
|
|
30
|
+
wuruiqi/cnki-mcp https://github.com/wuruiqi/cnki-mcp
|
|
31
|
+
|
|
32
|
+
All three are MIT-licensed. This project is MIT-licensed as well, so the terms are
|
|
33
|
+
compatible either way; the attribution above is given because it is the right
|
|
34
|
+
thing to do, not because a licence compels it for the three items listed.
|
|
35
|
+
|
|
36
|
+
What was NOT taken from them: the browser transport (they drive a debug port; this
|
|
37
|
+
project uses a browser extension plus a native messaging host), the microkernel
|
|
38
|
+
and channel layout, the policy/quota/risk engine, the parsers, the export layer,
|
|
39
|
+
and every line of Python, JavaScript, C# and PowerShell in this repository.
|
|
40
|
+
|
|
41
|
+
If you believe a fragment here is derived from your work in a way this notice does
|
|
42
|
+
not describe, open an issue and it will be corrected or removed.
|
|
43
|
+
|
|
44
|
+
See docs/reference-parity.md for a feature-by-feature comparison, including where
|
|
45
|
+
this project deliberately diverges.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Dependencies
|
|
49
|
+
------------
|
|
50
|
+
|
|
51
|
+
Runtime (installed by pip, not vendored into this repository):
|
|
52
|
+
|
|
53
|
+
mcp MIT
|
|
54
|
+
beautifulsoup4 MIT
|
|
55
|
+
lxml BSD-3-Clause
|
|
56
|
+
uvicorn BSD-3-Clause
|
|
57
|
+
packaging Apache-2.0 or BSD-2-Clause
|
|
58
|
+
|
|
59
|
+
Development only:
|
|
60
|
+
|
|
61
|
+
pytest, pytest-cov, ruff, mypy, pre-commit MIT
|
|
62
|
+
|
|
63
|
+
Licence texts are distributed with each package; the statements above follow the
|
|
64
|
+
metadata those packages publish.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
Trademarks
|
|
68
|
+
----------
|
|
69
|
+
|
|
70
|
+
"CNKI", "知网", and other names are trademarks of their respective owners.
|
|
71
|
+
They are used here only to describe interoperability targets, not as branding.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
Data and fixtures
|
|
75
|
+
-----------------
|
|
76
|
+
|
|
77
|
+
No bibliographic data, credentials, cookies, or session material is distributed
|
|
78
|
+
with this project.
|
|
79
|
+
|
|
80
|
+
The HTML files under tests/fixtures/ are page **structure** recorded from public
|
|
81
|
+
pages, then desensitised:
|
|
82
|
+
|
|
83
|
+
- abstracts, author names, affiliations and keyword labels were replaced with
|
|
84
|
+
synthetic placeholders;
|
|
85
|
+
- mail addresses and any session-scoped parameters were removed;
|
|
86
|
+
- what remains is markup, class names, and public bibliographic facts (article
|
|
87
|
+
titles, journal names, volume/issue/page numbers, DOIs).
|
|
88
|
+
|
|
89
|
+
They exist so the parsers can be tested offline without touching the site.
|