amenbo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +171 -0
- package/dist/adapters/index.d.ts +22 -0
- package/dist/adapters/index.js +51 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/types.d.ts +21 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cache.d.ts +90 -0
- package/dist/cache.js +286 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +46 -0
- package/dist/cli.js +327 -0
- package/dist/cli.js.map +1 -0
- package/dist/core.d.ts +78 -0
- package/dist/core.js +649 -0
- package/dist/core.js.map +1 -0
- package/dist/diff.d.ts +20 -0
- package/dist/diff.js +44 -0
- package/dist/diff.js.map +1 -0
- package/dist/errors.d.ts +97 -0
- package/dist/errors.js +209 -0
- package/dist/errors.js.map +1 -0
- package/dist/extract/dataSources.d.ts +10 -0
- package/dist/extract/dataSources.js +115 -0
- package/dist/extract/dataSources.js.map +1 -0
- package/dist/extract/geometry.d.ts +63 -0
- package/dist/extract/geometry.js +77 -0
- package/dist/extract/geometry.js.map +1 -0
- package/dist/extract/markdown.d.ts +24 -0
- package/dist/extract/markdown.js +221 -0
- package/dist/extract/markdown.js.map +1 -0
- package/dist/extract/outline.d.ts +33 -0
- package/dist/extract/outline.js +128 -0
- package/dist/extract/outline.js.map +1 -0
- package/dist/extract/pdf.d.ts +22 -0
- package/dist/extract/pdf.js +95 -0
- package/dist/extract/pdf.js.map +1 -0
- package/dist/extract/preview.d.ts +19 -0
- package/dist/extract/preview.js +94 -0
- package/dist/extract/preview.js.map +1 -0
- package/dist/extract/pruning.d.ts +61 -0
- package/dist/extract/pruning.js +101 -0
- package/dist/extract/pruning.js.map +1 -0
- package/dist/extract/qualityScore.d.ts +67 -0
- package/dist/extract/qualityScore.js +98 -0
- package/dist/extract/qualityScore.js.map +1 -0
- package/dist/fetcher/browser.d.ts +85 -0
- package/dist/fetcher/browser.js +343 -0
- package/dist/fetcher/browser.js.map +1 -0
- package/dist/fetcher/http.d.ts +105 -0
- package/dist/fetcher/http.js +538 -0
- package/dist/fetcher/http.js.map +1 -0
- package/dist/fetcher/index.d.ts +41 -0
- package/dist/fetcher/index.js +92 -0
- package/dist/fetcher/index.js.map +1 -0
- package/dist/jp/consentBanner.d.ts +27 -0
- package/dist/jp/consentBanner.js +48 -0
- package/dist/jp/consentBanner.js.map +1 -0
- package/dist/jp/normalize.d.ts +30 -0
- package/dist/jp/normalize.js +65 -0
- package/dist/jp/normalize.js.map +1 -0
- package/dist/links.d.ts +20 -0
- package/dist/links.js +223 -0
- package/dist/links.js.map +1 -0
- package/dist/politeness.d.ts +66 -0
- package/dist/politeness.js +170 -0
- package/dist/politeness.js.map +1 -0
- package/dist/screenshot.d.ts +38 -0
- package/dist/screenshot.js +90 -0
- package/dist/screenshot.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +111 -0
- package/dist/server.js.map +1 -0
- package/dist/templateLearning.d.ts +11 -0
- package/dist/templateLearning.js +42 -0
- package/dist/templateLearning.js.map +1 -0
- package/dist/tokens.d.ts +47 -0
- package/dist/tokens.js +141 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rererr
|
|
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.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# amenbo 🐜💧
|
|
2
|
+
|
|
3
|
+
**Skims the web without making waves** — a Japanese-web-native MCP server for low-impact, token-efficient web collection.
|
|
4
|
+
|
|
5
|
+
amenbo(アメンボ / water strider)は、Claude Code や Codex のようなコーディングエージェント向けの [MCP](https://modelcontextprotocol.io) サーバーです。水面に波を立てずに滑る虫のように、**収集先に負荷をかけず・少ないトークンで** Web から情報を集めます。とりわけ**日本語サイト**に最適化しています。MCP クライアントを持たないシェル環境からは、同じコアを共有する CLI としても使えます([CLIとして使う](#cliとして使う)参照)。
|
|
6
|
+
|
|
7
|
+
## なぜ amenbo か
|
|
8
|
+
|
|
9
|
+
汎用のスクレイピングツールの多くは英語圏の Web を前提に作られており、日本語サイトでは次のような取りこぼしが起きがちです。amenbo はこれらを正面から扱います。
|
|
10
|
+
|
|
11
|
+
- **構造化が甘いサイト** — div の入れ子やテーブルレイアウトが多い日本語サイトでも、レンダリング結果のジオメトリ(見た目の配置)から本文領域を推定します
|
|
12
|
+
- **文字化け** — Shift_JIS / EUC-JP / ISO-2022-JP を自動判別
|
|
13
|
+
- **ふりがな** — `<ruby>` の振り仮名を除去し、本文の二重化を防止
|
|
14
|
+
- **画像で出す情報** — 画像化された料金表やバナー中心のページは、テキスト抽出が貧弱なとき自動でスクリーンショットに切り替え
|
|
15
|
+
- **国内主要サイト** — Qiita / Zenn / note / はてなブログ / Yahoo!ニュース / PR TIMES / Wikipedia 日本語版に専用アダプタ
|
|
16
|
+
|
|
17
|
+
類似ツール(公式 fetch MCP / Jina Reader / Playwright MCP / PixelRAG pixelshot)との実測比較は、記事「[エージェントのWeb取得、ツール次第でトークンが5000倍違った話](https://zenn.dev/rererr_engineer/articles/e571e5b6eb1d53)」を参照してください。ハーネスと生ログは [`bench/`](./bench/) にあります。
|
|
18
|
+
|
|
19
|
+
## トークンを節約する仕組み
|
|
20
|
+
|
|
21
|
+
- **段階開示** — `mode: outline` で見出しツリーと各節のトークン量だけ先に返し、必要な節だけ `section` 指定で取得。長大なページを丸ごと流し込みません
|
|
22
|
+
- **CJK 対応の本文プルーニング** — 句読点密度・文字種比率・リンク密度でナビ/広告/フッターを除去
|
|
23
|
+
- **差分応答** — 一度取得した URL の再取得時、変更が無ければ `unchanged`、あれば変更された節だけを返します
|
|
24
|
+
- **自動 Markdown/画像切替** — 品質スコアが低いページだけスクリーンショットにし、壊れた Markdown を読ませて取り直す往復を避けます
|
|
25
|
+
- **CJK トークン見積り** — 日本語は英語よりトークン単価が重いため、文字種別の係数でページ分割の予算を計算します
|
|
26
|
+
|
|
27
|
+
## 収集先への低負荷
|
|
28
|
+
|
|
29
|
+
- **二段フェッチ** — まず素の HTTP GET。JS 描画が必要なページだけ headless Chromium に昇格するので、大半の取得でブラウザを起動しません
|
|
30
|
+
- **礼儀正しいクローラ** — robots.txt と Crawl-Delay を尊重、同一ドメインへは直列 + 既定 1 req/秒。リンク列挙は sitemap / RSS を優先しページを舐めません
|
|
31
|
+
- **正直な User-Agent** — ボットであることを明示します。**anti-bot 回避は実装しません**
|
|
32
|
+
- **キャッシュ** — ETag / If-Modified-Since で再検証し、無駄な再取得を避けます
|
|
33
|
+
|
|
34
|
+
## インストール
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g amenbo
|
|
38
|
+
# postinstall で Chromium が入ります(SPA描画・スクリーンショット用)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
または開発用途:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/Rererr/amenbo.git
|
|
45
|
+
cd amenbo
|
|
46
|
+
npm install
|
|
47
|
+
npm run build
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Claude Code への登録
|
|
51
|
+
|
|
52
|
+
`.mcp.json`(またはグローバル設定)に追加します:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"amenbo": {
|
|
58
|
+
"command": "amenbo"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
ローカルビルドを使う場合は `"command": "node", "args": ["/path/to/amenbo/dist/server.js"]`。
|
|
65
|
+
|
|
66
|
+
### エージェントに使い方を教える(推奨プロンプト)
|
|
67
|
+
|
|
68
|
+
ツール定義だけでは「段階開示で取る」といった使い方の作法までは伝わりません。以下を `CLAUDE.md` や `AGENTS.md` にコピペすると、エージェントが amenbo を効率よく使うようになります。
|
|
69
|
+
|
|
70
|
+
```markdown
|
|
71
|
+
## Web取得は amenbo を使う
|
|
72
|
+
|
|
73
|
+
- ページ取得は `fetch`(mode 既定 `auto`)。長そうなページや一部しか要らないページは、
|
|
74
|
+
まず `mode: "outline"` で見出しと各節のトークン量を確認し、必要な節だけ `section` 指定で取得する
|
|
75
|
+
- 同じ URL の再取得で `unchanged` / `diff` が返るのは正常(変更なし / 変更節のみ)。
|
|
76
|
+
全文が必要なときだけ `force_full: true` を使う
|
|
77
|
+
- サイト内のページを探すときは URL を推測せず `links`(`filter` で絞り込み)で列挙する
|
|
78
|
+
- シェルが使える環境で、キーワードで探したいだけの長いページや複数ページの一括収集は、
|
|
79
|
+
CLI で `amenbo fetch <url> > page.md` に落として grep / 部分読みする(本文をコンテキストに入れない)。
|
|
80
|
+
構造を見ながら判断したいページは従来どおり MCP の outline → section が向く
|
|
81
|
+
- 日本語以外のサイトにも使える(段階開示・キャッシュ・低負荷は言語非依存)。ただし本文抽出は
|
|
82
|
+
日本語向けに調整しているため、非日本語ページで本文が欠けて見えるときは `selector` 指定か
|
|
83
|
+
`mode: "screenshot"` で取り直す
|
|
84
|
+
- 料金表・レイアウトなど視覚情報が目的なら `screenshot`。`scale: 0.5` 程度で画像トークンを減らせる
|
|
85
|
+
- robots.txt 拒否や bot 対策による取得失敗は仕様(回避しない)。失敗はそのままユーザーに報告する
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## CLIとして使う
|
|
89
|
+
|
|
90
|
+
`amenbo` は MCP サーバーと同一のコア(取得・キャッシュ・politeness・抽出ロジック)を共有する CLI としても動作します。引数なし、または `amenbo serve` は従来通り MCP サーバーとして起動する(`.mcp.json` の `"command": "amenbo"` はそのまま動きます)ので、既存の MCP 登録には影響しません。
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# ページをMarkdownとして取得(標準出力へ)
|
|
94
|
+
amenbo fetch https://example.com/
|
|
95
|
+
|
|
96
|
+
# 長いページはまずoutlineで見出しとトークン量だけ確認
|
|
97
|
+
amenbo fetch https://example.com/ --mode outline
|
|
98
|
+
|
|
99
|
+
# 出力をファイルに落として grep や部分読み(head/sed)する
|
|
100
|
+
amenbo fetch https://example.com/ > page.md
|
|
101
|
+
grep -A3 "料金" page.md
|
|
102
|
+
|
|
103
|
+
# サイト内のリンクを列挙(sitemap/RSS優先)
|
|
104
|
+
amenbo links https://example.com/ --filter "blog/*"
|
|
105
|
+
|
|
106
|
+
# スクリーンショット(タイルPNGは--out-dirへ保存され、パスが標準出力に列挙される)
|
|
107
|
+
amenbo screenshot https://example.com/ --viewport-only --scale 0.5 --out-dir ./shots
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
各サブコマンドの詳細は `amenbo <fetch|links|screenshot> --help` を参照してください。
|
|
111
|
+
|
|
112
|
+
**MCP と CLI の使い分け**:
|
|
113
|
+
|
|
114
|
+
- **MCP** — エージェントの主経路。ブラウザ(Chromium)がプロセス内でウォームに保たれ、スクリーンショット等の画像を会話へ直接返せる。claude.ai のようにシェルを持たないホストのエージェントにも届く
|
|
115
|
+
- **CLI** — シェルスクリプト・CI・デバッグ用途、出力をファイルに落として `grep`/部分読みしたい場合、または MCP 非対応のエージェント/ツールチェーンから使う場合に向く。1 コマンド= 1 プロセスのためブラウザは毎回起動する
|
|
116
|
+
|
|
117
|
+
キャッシュ・差分応答(`unchanged`/`diff`)・レート制御(robots.txt/ドメイン毎の直列アクセス)の状態は MCP サーバーと CLI で共有されます(同じ `~/.cache/amenbo` を使うため)。ただしレート制御のプロセス間共有はベストエフォートです。同一ドメインへの直列化は各プロセス内でのみ厳密に保証され、MCP サーバーと複数の CLI 実行が同時に同じドメインへアクセスした場合、最小間隔が多少すり抜けることがあります。
|
|
118
|
+
|
|
119
|
+
## ツール
|
|
120
|
+
|
|
121
|
+
### `fetch` — ページを取得
|
|
122
|
+
| パラメータ | 説明 |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `url` | 取得対象 URL(http/https のみ。PDF 可) |
|
|
125
|
+
| `mode` | `auto`(既定・品質スコアで Markdown/screenshot 自動切替) / `markdown` / `outline`(見出し要約) / `screenshot` |
|
|
126
|
+
| `selector` | 本文を絞り込む CSS セレクタ |
|
|
127
|
+
| `section` | outline で得た section ID。その節の Markdown のみ返す |
|
|
128
|
+
| `page` | ページ番号(既定 1) |
|
|
129
|
+
| `max_tokens` | 1 ページの概算トークン上限(既定 8000) |
|
|
130
|
+
| `force_full` | true で差分応答・定型ブロック除去を無効化し常に全文を返す |
|
|
131
|
+
|
|
132
|
+
### `links` — リンクを列挙
|
|
133
|
+
| パラメータ | 説明 |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `url` | 起点 URL |
|
|
136
|
+
| `filter` | URL/リンクテキストの部分一致、または `*` を使った glob |
|
|
137
|
+
|
|
138
|
+
sitemap → RSS/Atom → ページ内リンクの順で探索します。
|
|
139
|
+
|
|
140
|
+
### `screenshot` — スクリーンショット
|
|
141
|
+
| パラメータ | 説明 |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `url` | 撮影対象 URL(http/https のみ) |
|
|
144
|
+
| `fullPage` | 既定 true。false で最初のビューポート分のみ |
|
|
145
|
+
| `width` | タイル幅 px(既定 1280) |
|
|
146
|
+
| `scale` | 解像度スケール 0.5〜1.0(既定 1.0)。小さいほど画像トークン減 |
|
|
147
|
+
|
|
148
|
+
## 環境変数
|
|
149
|
+
|
|
150
|
+
| 変数 | 既定 | 説明 |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| `AMENBO_CACHE_DIR` | `~/.cache/amenbo` | キャッシュ(SQLite + PNG)の保存先 |
|
|
153
|
+
| `AMENBO_CACHE_TTL_MS` | `900000`(15分) | キャッシュの有効期限 |
|
|
154
|
+
| `AMENBO_MAX_BODY_BYTES` | `20971520`(20MB) | 取得ボディの上限サイズ |
|
|
155
|
+
|
|
156
|
+
## セキュリティ
|
|
157
|
+
|
|
158
|
+
- **SSRF 対策** — http/https 以外のスキーム(`file:`, `ftp:` 等)を拒否。DNS 解決した接続先が private / loopback / link-local / 予約アドレスなら拒否。DNS rebinding(TOCTOU)対策として実接続を検証済み IP に固定します
|
|
159
|
+
- **ボディサイズ上限** — 巨大レスポンスによる OOM を防止
|
|
160
|
+
|
|
161
|
+
## 開発
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm run typecheck # strict 型チェック
|
|
165
|
+
npm test # vitest
|
|
166
|
+
npm run build # dist/ へビルド
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## ライセンス
|
|
170
|
+
|
|
171
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/index.ts — J7 国内サイトアダプタの初期セット。
|
|
3
|
+
*
|
|
4
|
+
* plan.md指定の初期セット(Qiita/Zenn/note/はてなブログ系/Yahoo!ニュース/PR TIMES)を実装する。
|
|
5
|
+
* 各セレクタは実際に対象サイトのHTMLを取得して確認済み(2026-07時点)。
|
|
6
|
+
* サイトのマークアップ変更で機能しなくなった場合は、contentSelectorsが1件もヒットせず
|
|
7
|
+
* 通常のReadabilityパスへフォールバックする(extract/markdown.ts側の挙動)ため、
|
|
8
|
+
* アダプタの陳腐化が抽出失敗に直結しない設計になっている。
|
|
9
|
+
*
|
|
10
|
+
* 設計判断: plan.mdは「e-Gov等」にも触れているが、e-Govは法令検索という性質上
|
|
11
|
+
* ページ構造がクエリ毎に大きく異なり単一セレクタでの決定的抽出が難しいため、
|
|
12
|
+
* Phase 3の初期セットからは見送り、確認が取れた6サイトのみを実装する。
|
|
13
|
+
*
|
|
14
|
+
* Phase 4追加: ja.wikipedia.org。Readabilityが記事中の全<h2>-<h6>見出しを剥ぎ落として
|
|
15
|
+
* しまう(MediaWikiが見出しを`<div class="mw-heading">`等でラップし編集リンクを併記する
|
|
16
|
+
* 構造をReadabilityの本文整形ロジックが正しく扱えないため)ことを実機検証で確認しており、
|
|
17
|
+
* アダプタでReadabilityを完全にバイパスすることで見出し構造を保つ(outlineモードの実用性に直結)。
|
|
18
|
+
*/
|
|
19
|
+
import type { SiteAdapter } from "./types.js";
|
|
20
|
+
export declare const SITE_ADAPTERS: SiteAdapter[];
|
|
21
|
+
/** ホスト名(document.location.hostname相当)に一致するアダプタを探す。無ければnull。 */
|
|
22
|
+
export declare function findAdapter(hostname: string): SiteAdapter | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const SITE_ADAPTERS = [
|
|
2
|
+
{
|
|
3
|
+
name: "qiita",
|
|
4
|
+
hostPattern: /(^|\.)qiita\.com$/i,
|
|
5
|
+
contentSelectors: [".it-MdContent", "article"],
|
|
6
|
+
notes: "記事本文。.it-MdContentはQiita記法のレンダリング結果。",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: "zenn",
|
|
10
|
+
hostPattern: /(^|\.)zenn\.dev$/i,
|
|
11
|
+
contentSelectors: [".znc", "article"],
|
|
12
|
+
notes: "記事本文。.zncはZenn Markdownのレンダリング結果。",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "note",
|
|
16
|
+
hostPattern: /(^|\.)note\.com$/i,
|
|
17
|
+
contentSelectors: [".note-common-styles__textnote-body", ".p-article__body", "article"],
|
|
18
|
+
notes: "note記事本文。textnote-bodyが本文のみに最も絞られたコンテナ。",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "hatenablog",
|
|
22
|
+
hostPattern: /(^|\.)hatenablog\.com$|(^|\.)hateblo\.jp$/i,
|
|
23
|
+
contentSelectors: [".entry-content", "article"],
|
|
24
|
+
removeSelectors: [".hatena-module-ad", ".advertisement-unit"],
|
|
25
|
+
notes: "はてなブログの記事本文(*.hatenablog.com / *.hateblo.jp)。",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "yahoo-news",
|
|
29
|
+
hostPattern: /(^|\.)news\.yahoo\.co\.jp$/i,
|
|
30
|
+
contentSelectors: [".article_body", "article#uamods", "article"],
|
|
31
|
+
notes: "Yahoo!ニュース個別記事の本文。",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "prtimes",
|
|
35
|
+
hostPattern: /(^|\.)prtimes\.jp$/i,
|
|
36
|
+
contentSelectors: ["#press-release-body", "article"],
|
|
37
|
+
notes: "PR TIMESリリース本文。",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "wikipedia-ja",
|
|
41
|
+
hostPattern: /(^|\.)ja\.wikipedia\.org$/i,
|
|
42
|
+
contentSelectors: [".mw-parser-output"],
|
|
43
|
+
removeSelectors: [".mw-editsection", ".navbox", ".ambox", ".hatnote", ".noprint", ".mw-empty-elt"],
|
|
44
|
+
notes: "Wikipedia日本語版の本文(MediaWiki出力)。Readabilityが見出しを剥がすため必須のアダプタ。",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
/** ホスト名(document.location.hostname相当)に一致するアダプタを探す。無ければnull。 */
|
|
48
|
+
export function findAdapter(hostname) {
|
|
49
|
+
return SITE_ADAPTERS.find((adapter) => adapter.hostPattern.test(hostname)) ?? null;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C;QACE,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,oBAAoB;QACjC,gBAAgB,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC;QAC9C,KAAK,EAAE,sCAAsC;KAC9C;IACD;QACE,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,mBAAmB;QAChC,gBAAgB,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;QACrC,KAAK,EAAE,mCAAmC;KAC3C;IACD;QACE,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,mBAAmB;QAChC,gBAAgB,EAAE,CAAC,oCAAoC,EAAE,kBAAkB,EAAE,SAAS,CAAC;QACvF,KAAK,EAAE,yCAAyC;KACjD;IACD;QACE,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,4CAA4C;QACzD,gBAAgB,EAAE,CAAC,gBAAgB,EAAE,SAAS,CAAC;QAC/C,eAAe,EAAE,CAAC,mBAAmB,EAAE,qBAAqB,CAAC;QAC7D,KAAK,EAAE,+CAA+C;KACvD;IACD;QACE,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,6BAA6B;QAC1C,gBAAgB,EAAE,CAAC,eAAe,EAAE,gBAAgB,EAAE,SAAS,CAAC;QAChE,KAAK,EAAE,oBAAoB;KAC5B;IACD;QACE,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,qBAAqB;QAClC,gBAAgB,EAAE,CAAC,qBAAqB,EAAE,SAAS,CAAC;QACpD,KAAK,EAAE,iBAAiB;KACzB;IACD;QACE,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,4BAA4B;QACzC,gBAAgB,EAAE,CAAC,mBAAmB,CAAC;QACvC,eAAe,EAAE,CAAC,iBAAiB,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,CAAC;QAClG,KAAK,EAAE,6DAA6D;KACrE;CACF,CAAC;AAEF,+DAA+D;AAC/D,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;AACrF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/types.ts — J7 国内サイトアダプタの型定義。
|
|
3
|
+
*
|
|
4
|
+
* 宣言的なTSオブジェクトでドメイン→本文セレクタを定義する(plan.md記載は「宣言的YAML」だが、
|
|
5
|
+
* 型チェック・IDE補完の恩恵と外部パーサ追加不要という理由からTSオブジェクトで実装する)。
|
|
6
|
+
*/
|
|
7
|
+
export interface SiteAdapter {
|
|
8
|
+
/** メタデータ `adapter: <name>` に出力される識別名。 */
|
|
9
|
+
name: string;
|
|
10
|
+
/** ホスト名にマッチする正規表現。 */
|
|
11
|
+
hostPattern: RegExp;
|
|
12
|
+
/**
|
|
13
|
+
* 本文として採用するCSSセレクタ候補(先頭から順に試し、最初に要素が見つかったものを使う)。
|
|
14
|
+
* Readabilityより優先される。
|
|
15
|
+
*/
|
|
16
|
+
contentSelectors: string[];
|
|
17
|
+
/** 本文セレクタでマッチした要素の中から、さらに除去するCSSセレクタ(コメント欄・広告枠等)。 */
|
|
18
|
+
removeSelectors?: string[];
|
|
19
|
+
/** 補足メモ(保守用、実行時には使わない)。 */
|
|
20
|
+
notes?: string;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":""}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export type CacheStatus = "fresh" | "revalidated" | "miss";
|
|
2
|
+
export type ScreenshotCacheStatus = "fresh" | "miss";
|
|
3
|
+
export interface CacheEntry {
|
|
4
|
+
url: string;
|
|
5
|
+
etag: string | null;
|
|
6
|
+
lastModified: string | null;
|
|
7
|
+
contentHash: string;
|
|
8
|
+
markdown: string;
|
|
9
|
+
/** メタデータ(title/finalUrl/tier等)をJSON文字列として保存したものをパースした値。 */
|
|
10
|
+
metadata: Record<string, unknown>;
|
|
11
|
+
fetchedAt: number;
|
|
12
|
+
}
|
|
13
|
+
export interface CacheWriteInput {
|
|
14
|
+
url: string;
|
|
15
|
+
etag: string | null;
|
|
16
|
+
lastModified: string | null;
|
|
17
|
+
markdown: string;
|
|
18
|
+
metadata: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export declare function hashContent(content: string): string;
|
|
21
|
+
/** スクリーンショットのキャッシュキー(url+撮影パラメータの組)を計算する。 */
|
|
22
|
+
export declare function computeScreenshotCacheKey(url: string, width: number, scale: number, fullPage: boolean): string;
|
|
23
|
+
export interface ScreenshotCacheEntry {
|
|
24
|
+
cacheKey: string;
|
|
25
|
+
url: string;
|
|
26
|
+
tilePaths: string[];
|
|
27
|
+
metadata: Record<string, unknown>;
|
|
28
|
+
fetchedAt: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ScreenshotWriteInput {
|
|
31
|
+
cacheKey: string;
|
|
32
|
+
url: string;
|
|
33
|
+
tiles: Buffer[];
|
|
34
|
+
metadata: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
export declare class PageCache {
|
|
37
|
+
private readonly db;
|
|
38
|
+
private readonly cacheDir;
|
|
39
|
+
private readonly ttlMs;
|
|
40
|
+
private readonly now;
|
|
41
|
+
constructor(options?: {
|
|
42
|
+
dbPath?: string;
|
|
43
|
+
cacheDir?: string;
|
|
44
|
+
ttlMs?: number;
|
|
45
|
+
now?: () => number;
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* M3: TTL超過エントリを削除する(ディスク無制限増加の防止)。起動時に自動実行される。
|
|
49
|
+
* screenshotsはSQLite行に加え、対応するPNGファイル(タイル)もあわせて削除する。
|
|
50
|
+
*/
|
|
51
|
+
private pruneExpired;
|
|
52
|
+
/** 保存済みエントリを取得する(TTL判定は行わない。判定は isFresh を使う)。 */
|
|
53
|
+
get(url: string): CacheEntry | undefined;
|
|
54
|
+
/** エントリ(ページ/スクリーンショットいずれも可)がTTL内(再検証不要)かどうかを判定する。 */
|
|
55
|
+
isFresh(entry: {
|
|
56
|
+
fetchedAt: number;
|
|
57
|
+
}): boolean;
|
|
58
|
+
/** 保存済みスクリーンショットエントリを取得する(TTL判定は isFresh を使う)。 */
|
|
59
|
+
getScreenshot(cacheKey: string): ScreenshotCacheEntry | undefined;
|
|
60
|
+
/** スクリーンショットのタイル群をファイル保存し、パスをSQLiteへ記録する。 */
|
|
61
|
+
setScreenshot(input: ScreenshotWriteInput): ScreenshotCacheEntry;
|
|
62
|
+
/** 新規取得結果を保存(既存があれば上書き)する。 */
|
|
63
|
+
set(input: CacheWriteInput): CacheEntry;
|
|
64
|
+
/** 304再検証成功時: 本文は変えず取得時刻のみ更新する(再変換しない)。 */
|
|
65
|
+
touch(url: string): void;
|
|
66
|
+
/** Phase 4テンプレート学習: このページの(除去前・全)ブロックハッシュ集合をドメイン別に記録する。 */
|
|
67
|
+
recordDomainPageBlocks(domain: string, url: string, blockHashes: string[]): void;
|
|
68
|
+
/**
|
|
69
|
+
* 直近recentPages件のページ全てに共通して出現したブロックハッシュを定型ブロック
|
|
70
|
+
* (ヘッダ/フッタ/定型ナビ等)とみなして返す。まだrecentPages件分のページ履歴が
|
|
71
|
+
* 無い場合(コールドスタート)は誤除去を避けるため空集合を返す。
|
|
72
|
+
*/
|
|
73
|
+
getTemplateBlockHashes(domain: string, recentPages?: number): Set<string>;
|
|
74
|
+
/**
|
|
75
|
+
* CLI併設対応: ホスト(例 "example.com")の最終リクエスト時刻を返す(未記録ならnull)。
|
|
76
|
+
* PolitenessManagerのstoreオプションとして注入し、プロセス間でレート制御状態を共有するために使う。
|
|
77
|
+
*/
|
|
78
|
+
getHostLastRequestAt(host: string): number | null;
|
|
79
|
+
/**
|
|
80
|
+
* ホストの最終リクエスト時刻を記録する。
|
|
81
|
+
*
|
|
82
|
+
* code-reviewer指摘: MCPサーバー/CLIの複数プロセスが同一ホストへほぼ同時にwaitTurnを
|
|
83
|
+
* 実行すると、「読み取り→待機→書き込み」の3ステップがプロセス間でアトミックではないため
|
|
84
|
+
* 後勝ちの書き込みが古い時刻で新しい時刻を上書きし、最小間隔がすり抜けうる
|
|
85
|
+
* (完全な直列化はプロセス内のみ。プロセス間はベストエフォート。PolitenessManagerの
|
|
86
|
+
* store JSDoc参照)。せめて時刻が巻き戻らないよう、SQL側でMAXを取って単調増加を保証する。
|
|
87
|
+
*/
|
|
88
|
+
setHostLastRequestAt(host: string, at: number): void;
|
|
89
|
+
close(): void;
|
|
90
|
+
}
|