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.
Files changed (83) hide show
  1. wensu-0.4.0/.gitignore +42 -0
  2. wensu-0.4.0/AGENTS.md +37 -0
  3. wensu-0.4.0/CHANGELOG.md +470 -0
  4. wensu-0.4.0/LICENSE +21 -0
  5. wensu-0.4.0/NOTICE +89 -0
  6. wensu-0.4.0/PKG-INFO +314 -0
  7. wensu-0.4.0/README.md +262 -0
  8. wensu-0.4.0/docs/acceptance.md +108 -0
  9. wensu-0.4.0/docs/adr/0001-microkernel.md +22 -0
  10. wensu-0.4.0/docs/adr/0002-read-only.md +19 -0
  11. wensu-0.4.0/docs/adr/0003-native-bridge.md +21 -0
  12. wensu-0.4.0/docs/adr/0004-risk-stop.md +26 -0
  13. wensu-0.4.0/docs/adr/0005-quota-300.md +44 -0
  14. wensu-0.4.0/docs/adr/0006-no-third-party-ingest.md +38 -0
  15. wensu-0.4.0/docs/architecture.md +211 -0
  16. wensu-0.4.0/docs/bridge-protocol.md +109 -0
  17. wensu-0.4.0/docs/browser-setup.md +155 -0
  18. wensu-0.4.0/docs/channel-contract.md +73 -0
  19. wensu-0.4.0/docs/cnki-maintenance.md +120 -0
  20. wensu-0.4.0/docs/incidents.md +98 -0
  21. wensu-0.4.0/docs/integration/codex.md +169 -0
  22. wensu-0.4.0/docs/integration/dsh.md +47 -0
  23. wensu-0.4.0/docs/integration/zotero.md +99 -0
  24. wensu-0.4.0/docs/operations.md +174 -0
  25. wensu-0.4.0/docs/publishing.md +153 -0
  26. wensu-0.4.0/docs/reference-parity.md +54 -0
  27. wensu-0.4.0/extension/background.js +186 -0
  28. wensu-0.4.0/extension/content/actions.js +281 -0
  29. wensu-0.4.0/extension/manifest.json +34 -0
  30. wensu-0.4.0/native-host/README.md +46 -0
  31. wensu-0.4.0/native-host/install.ps1 +97 -0
  32. wensu-0.4.0/native-host/launcher.cs +91 -0
  33. wensu-0.4.0/native-host/main.py +249 -0
  34. wensu-0.4.0/pyproject.toml +131 -0
  35. wensu-0.4.0/schemas/record.json +28 -0
  36. wensu-0.4.0/schemas/tools.json +58 -0
  37. wensu-0.4.0/skills/scholar-detail/SKILL.md +54 -0
  38. wensu-0.4.0/skills/scholar-export/SKILL.md +49 -0
  39. wensu-0.4.0/skills/scholar-install/SKILL.md +160 -0
  40. wensu-0.4.0/skills/scholar-search/SKILL.md +117 -0
  41. wensu-0.4.0/src/scholar_search/__init__.py +6 -0
  42. wensu-0.4.0/src/scholar_search/adapters/__init__.py +1 -0
  43. wensu-0.4.0/src/scholar_search/adapters/bridge/__init__.py +150 -0
  44. wensu-0.4.0/src/scholar_search/adapters/bridge/host.py +187 -0
  45. wensu-0.4.0/src/scholar_search/adapters/bridge/policy.py +83 -0
  46. wensu-0.4.0/src/scholar_search/adapters/cdp/__init__.py +1 -0
  47. wensu-0.4.0/src/scholar_search/adapters/fixture/__init__.py +68 -0
  48. wensu-0.4.0/src/scholar_search/assets.py +42 -0
  49. wensu-0.4.0/src/scholar_search/bootstrap.py +51 -0
  50. wensu-0.4.0/src/scholar_search/channels/__init__.py +1 -0
  51. wensu-0.4.0/src/scholar_search/channels/cnki/__init__.py +1 -0
  52. wensu-0.4.0/src/scholar_search/channels/cnki/manifest.json +20 -0
  53. wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/__init__.py +98 -0
  54. wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/doc_types.py +69 -0
  55. wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/parse_detail.py +169 -0
  56. wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/parse_search.py +96 -0
  57. wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/plan_detail.py +22 -0
  58. wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/plan_search.py +180 -0
  59. wensu-0.4.0/src/scholar_search/channels/cnki/v2026_10/selectors.py +53 -0
  60. wensu-0.4.0/src/scholar_search/cli.py +97 -0
  61. wensu-0.4.0/src/scholar_search/core/__init__.py +1 -0
  62. wensu-0.4.0/src/scholar_search/core/audit.py +70 -0
  63. wensu-0.4.0/src/scholar_search/core/cache.py +72 -0
  64. wensu-0.4.0/src/scholar_search/core/config.py +100 -0
  65. wensu-0.4.0/src/scholar_search/core/contracts.py +302 -0
  66. wensu-0.4.0/src/scholar_search/core/errors.py +85 -0
  67. wensu-0.4.0/src/scholar_search/core/export.py +217 -0
  68. wensu-0.4.0/src/scholar_search/core/locking.py +89 -0
  69. wensu-0.4.0/src/scholar_search/core/params.py +84 -0
  70. wensu-0.4.0/src/scholar_search/core/planner.py +86 -0
  71. wensu-0.4.0/src/scholar_search/core/policy.py +229 -0
  72. wensu-0.4.0/src/scholar_search/core/registry.py +135 -0
  73. wensu-0.4.0/src/scholar_search/core/risk.py +145 -0
  74. wensu-0.4.0/src/scholar_search/core/service.py +230 -0
  75. wensu-0.4.0/src/scholar_search/server.py +87 -0
  76. wensu-0.4.0/src/scholar_search/transports/__init__.py +1 -0
  77. wensu-0.4.0/src/scholar_search/transports/http.py +87 -0
  78. wensu-0.4.0/src/scholar_search/transports/stdio.py +13 -0
  79. wensu-0.4.0/tests/fixtures/cnki_detail_2026_10.html +15 -0
  80. wensu-0.4.0/tests/fixtures/cnki_detail_online_metadata_2026_10.html +13 -0
  81. wensu-0.4.0/tests/fixtures/cnki_detail_print_2026_10.html +19 -0
  82. wensu-0.4.0/tests/fixtures/cnki_search_2026_10.html +4 -0
  83. 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
+
@@ -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.