@hy-sde-org/dsh-web-search-public 0.1.0-rc.6

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 (57) hide show
  1. package/CONTRIBUTING.md +88 -0
  2. package/LICENSE +23 -0
  3. package/README.md +226 -0
  4. package/SECURITY.md +25 -0
  5. package/THIRD-PARTY-NOTICES.md +44 -0
  6. package/cordis.patch.yml +12 -0
  7. package/dist/engines/duckduckgo.d.ts +18 -0
  8. package/dist/engines/duckduckgo.d.ts.map +1 -0
  9. package/dist/engines/duckduckgo.js +77 -0
  10. package/dist/engines/duckduckgo.js.map +1 -0
  11. package/dist/engines/ecosia.d.ts +16 -0
  12. package/dist/engines/ecosia.d.ts.map +1 -0
  13. package/dist/engines/ecosia.js +42 -0
  14. package/dist/engines/ecosia.js.map +1 -0
  15. package/dist/engines/google.d.ts +19 -0
  16. package/dist/engines/google.d.ts.map +1 -0
  17. package/dist/engines/google.js +82 -0
  18. package/dist/engines/google.js.map +1 -0
  19. package/dist/engines/html.d.ts +41 -0
  20. package/dist/engines/html.d.ts.map +1 -0
  21. package/dist/engines/html.js +154 -0
  22. package/dist/engines/html.js.map +1 -0
  23. package/dist/engines/http.d.ts +22 -0
  24. package/dist/engines/http.d.ts.map +1 -0
  25. package/dist/engines/http.js +31 -0
  26. package/dist/engines/http.js.map +1 -0
  27. package/dist/engines/index.d.ts +8 -0
  28. package/dist/engines/index.d.ts.map +1 -0
  29. package/dist/engines/index.js +30 -0
  30. package/dist/engines/index.js.map +1 -0
  31. package/dist/engines/mojeek.d.ts +16 -0
  32. package/dist/engines/mojeek.d.ts.map +1 -0
  33. package/dist/engines/mojeek.js +56 -0
  34. package/dist/engines/mojeek.js.map +1 -0
  35. package/dist/engines/startpage.d.ts +19 -0
  36. package/dist/engines/startpage.d.ts.map +1 -0
  37. package/dist/engines/startpage.js +78 -0
  38. package/dist/engines/startpage.js.map +1 -0
  39. package/dist/index.d.ts +36 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +46 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/invariant.d.ts +16 -0
  44. package/dist/invariant.d.ts.map +1 -0
  45. package/dist/invariant.js +22 -0
  46. package/dist/invariant.js.map +1 -0
  47. package/dist/provider.d.ts +34 -0
  48. package/dist/provider.d.ts.map +1 -0
  49. package/dist/provider.js +87 -0
  50. package/dist/provider.js.map +1 -0
  51. package/dist/types.d.ts +34 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +29 -0
  54. package/dist/types.js.map +1 -0
  55. package/docs/operations.md +57 -0
  56. package/package.json +88 -0
  57. package/scripts/release-public.sh +83 -0
@@ -0,0 +1,88 @@
1
+ # Contributing
2
+
3
+ ## Prerequisites
4
+
5
+ - Node.js 22.19 or newer
6
+ - npm
7
+
8
+ Install exactly from the lockfile:
9
+
10
+ ```bash
11
+ npm ci
12
+ ```
13
+
14
+ ## Validation
15
+
16
+ Every behavioral change needs a direct regression test covering normal, edge, and failure
17
+ behavior. Run the same checks as CI:
18
+
19
+ ```bash
20
+ npm run check
21
+ npm test
22
+ npm run build
23
+ npm run lint:package
24
+ npm pack --dry-run
25
+ npm audit --omit=dev
26
+ ```
27
+
28
+ Parser tests must use fixture HTML, never the live web — a challenged or redesigned engine
29
+ page must not make the suite red. Live scraping is covered by the opt-in real test only:
30
+
31
+ ```bash
32
+ DSH_WEB_SEARCH_REAL_E2E=1 npm run test:real
33
+ ```
34
+
35
+ The real test needs outbound network access, tolerates engine bot challenges (including the
36
+ 403 response Ecosia returns for headless clients), and treats a changed organic-result set as
37
+ expected — record the observed engine behavior (per-engine result counts, challenge states)
38
+ in the commit message when you validate a parser change against live sites.
39
+
40
+ ## Public release
41
+
42
+ The package is published as a prebuilt public npm artifact. Do not publish TypeScript sources
43
+ or ask users to approve a Git dependency’s build script.
44
+
45
+ Run the non-publishing release gate from a clean worktree:
46
+
47
+ ```bash
48
+ npm run release:check
49
+ ```
50
+
51
+ The release owner must be a member of the npm `deepseek-ai` organization, enable 2FA, and log
52
+ in locally. Never send an npm password, OTP, or token through chat or commit it to the
53
+ repository. To run the same gate and publish the exact `package.json` version:
54
+
55
+ ```bash
56
+ npm login
57
+ npm run release:publish
58
+ ```
59
+
60
+ The script refuses a dirty worktree, an unexpected package name, a missing npm login, and a
61
+ version that already exists. It runs the complete validation suite, shows the tarball
62
+ contents, prompts for confirmation, and publishes with `--access public` to the public npm
63
+ registry.
64
+
65
+ After publication, verify the registry artifact rather than the local checkout:
66
+
67
+ ```bash
68
+ npm view @hy-sde-org/dsh-web-search-public version
69
+ dsh plugin --profile web add @hy-sde-org/dsh-web-search-public
70
+ dsh web --dump-config
71
+ ```
72
+
73
+ ## Design constraints
74
+
75
+ - Keep the chain credential-free. Never add an API-key path, even as a fallback.
76
+ - Keep engines strictly sequential and fail-forward: zero results, a timed-out engine, and a
77
+ failed request all advance to the next engine; only an all-engine failure may throw.
78
+ - Keep every engine secret-free and header-lean: the browser-shaped `userAgent` constant and
79
+ an `accept` header, nothing more.
80
+ - Keep parsers tolerant: a challenged or redesigned page must parse to zero organic results,
81
+ never throw into the chain.
82
+ - Keep tests fixture-based; the live suite is opt-in (`DSH_WEB_SEARCH_REAL_E2E=1`).
83
+ - Preserve the web seam contracts: providers return fully-formed `WebSearchSource[]` and the
84
+ consumer sets `truncated`; never mark results truncated yourself.
85
+ - Preserve `redirect: "error"` on every engine fetch.
86
+ - Preserve the oh-my-pi attribution and its MIT notice ([THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)) whenever engine code is changed or extended.
87
+
88
+ Changes to these constraints require updating this file and [docs/operations.md](docs/operations.md).
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mario Zechner
4
+ Copyright (c) 2025-2026 Can Bölük
5
+ Copyright (c) 2026 hy-sde
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # dsh-web-search-public — credential-free web search for DeepSeek Harness
2
+
3
+ A zero-API-key web search provider for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
4
+ The model-facing `web_search` tool runs a **sequential chain — Startpage first, then
5
+ DuckDuckGo → Ecosia → Google → Mojeek** — and returns the first engine that yields organic
6
+ results. No API keys, no accounts, no fetch provider, no setup.
7
+
8
+ | Identity | Value |
9
+ | --- | --- |
10
+ | Package | `@hy-sde-org/dsh-web-search-public` |
11
+ | Plugin id | `web-search-public` |
12
+ | Provider id | `public` |
13
+ | Engines (in order) | `startpage`, `duckduckgo`, `ecosia`, `google`, `mojeek` |
14
+ > **Based on [oh-my-pi](https://github.com/can1357/oh-my-pi)** — the credential-free engine
15
+ > chain and per-engine scrapers are ported from oh-my-pi’s `web/search/providers`
16
+ > (`public.ts`, `startpage.ts`, `duckduckgo.ts`, `ecosia.ts`, `google.ts`, `mojeek.ts`),
17
+ > adapted to the DeepSeek Harness `ctx.web` seam with sequential fail-forward semantics.
18
+ > oh-my-pi is MIT-licensed (Mario Zechner, Can Bölük); see
19
+ > [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).
20
+
21
+ ## Why
22
+
23
+ DeepSeek Harness ships search providers that need an API key (DeepSeek, Exa, Perplexity).
24
+ This bundle is the credential-free default: each search is an anonymous request to a public
25
+ search engine, and the chain fails forward. If Startpage is down, rate-limited, or serves a
26
+ bot challenge, the chain automatically tries DuckDuckGo, then Ecosia, then Google, then
27
+ Mojeek. The first engine that returns at least one organic result wins; engines return
28
+ zero results (rather than errors) when challenged, so the chain keeps moving instead of
29
+ failing.
30
+
31
+ ## Prerequisites
32
+
33
+ - Node.js 22.19 or newer with npm and pnpm on `PATH`;
34
+ - DeepSeek Harness `0.1.0-rc.6` including the standard `dsh` CLI;
35
+ - no API keys — nothing else.
36
+
37
+ Install the Harness CLI and pnpm before continuing:
38
+
39
+ ```bash
40
+ npm install --global @deepseek-ai/dsh@0.1.0-rc.6 pnpm
41
+ dsh --version
42
+ ```
43
+
44
+ ## Quick start
45
+
46
+ ### Route A — published npm package (recommended)
47
+
48
+ ```bash
49
+ dsh plugin --profile web add @hy-sde-org/dsh-web-search-public
50
+ ```
51
+
52
+ ### Route B — from source (validate this checkout or hack on the plugin)
53
+
54
+ ```bash
55
+ git clone <this-repository-url> dsh-web-search-public
56
+ cd dsh-web-search-public
57
+ npm ci
58
+ PACKAGE_TARBALL="$(npm pack --silent)"
59
+ dsh plugin --profile web add "$PWD/$PACKAGE_TARBALL"
60
+ cd ..
61
+ ```
62
+
63
+ `npm pack` runs the normal `prepack` build and produces a tarball containing `dist/`. A direct
64
+ `github:<this-repo>` dependency does not contain built output and is not a supported install
65
+ path — always install the built tarball (or the published package).
66
+
67
+ ### Verify the composed configuration
68
+
69
+ ```bash
70
+ dsh web --dump-config
71
+ ```
72
+
73
+ The composed tree must show `web.config.searchProvider: public` and a `web-search-public`
74
+ plugin row loading `@hy-sde-org/dsh-web-search-public`.
75
+
76
+ ### Run
77
+
78
+ ```bash
79
+ dsh web
80
+ ```
81
+
82
+ Ask the agent to `web_search` something. The tool returns rendered sources from the first
83
+ engine that answered.
84
+
85
+ ### Uninstall
86
+
87
+ ```bash
88
+ dsh plugin --profile web remove @hy-sde-org/dsh-web-search-public
89
+ ```
90
+
91
+ > **Already shipped?** If your harness checkout already mounts `web-search-public` in its base
92
+ > bundle (a future release may adopt this package as the shipped default), skip installation
93
+ > — it is already the active provider. Adding this bundle on top would duplicate the loader
94
+ > row and fail at boot (“duplicate loader entry id”).
95
+
96
+ ## What the bundle does
97
+
98
+ The package declares a DSH bundle (`dsh.bundle.patch` → `cordis.patch.yml`), so `dsh plugin`
99
+ installs it and applies its patch layer to the profile:
100
+
101
+ 1. sets `web.config.searchProvider` to `public` (making the credential-free chain the default),
102
+ 2. inserts the `web-search-public` plugin row.
103
+
104
+ The keyed DeepSeek route (`web-search-deepseek`, provider id `deepseek-official`) is **not**
105
+ disabled — deployments that prefer it keep their explicit choice by setting
106
+ `web.searchProvider: deepseek-official` and providing the `DEEPSEEK_API_KEY` credential.
107
+ No fetch provider is mounted and `tool-web` keeps `fetch: false`: the model can search, not
108
+ fetch arbitrary URLs, which defers SSRF exposure entirely.
109
+
110
+ ## How the chain works
111
+
112
+ `PublicSearchProvider.search()` walks `engines` in configured order:
113
+
114
+ ```text
115
+ startpage ──► duckduckgo ──► ecosia ──► google ──► mojeek
116
+ │ │ │ │ │
117
+ ▼ ▼ ▼ ▼ ▼
118
+ 1+ results 1+ results 1+ results 1+ results 1+ results → return them
119
+ │ │ │ │ │
120
+ ▼ ▼ ▼ ▼ ▼
121
+ 0 0 0 0 0 → all failed → WEB_PROVIDER_ERROR
122
+ ```
123
+
124
+ On each step the provider opens a fresh `AbortSignal.timeout(timeoutMs)` race, so a hung
125
+ engine cannot pin the call past its budget. The worst case for the default order is
126
+ `5 × timeoutMs` = 50 seconds; `dsh-tool-web` books a 60-second search budget for that.
127
+
128
+ - `available()` is `true` whenever at least one engine is configured — the chain is always
129
+ usable, which is the point.
130
+ - A caller abort (`signal.aborted`) aborts the in-flight engine and throws `WEB_ABORTED`.
131
+ - Only if **every** engine fails does the provider throw `WEB_PROVIDER_ERROR` with each
132
+ engine’s reason (`id: <reason>; …`). Zero organic results, a timed-out engine, and a failed
133
+ HTTP request all count as “this engine has nothing” and move to the next one.
134
+
135
+ ## Configuration
136
+
137
+ All options are optional; constants fill the defaults. Configure via the plugin row’s
138
+ `config:` in your profile patch, or programmatically via `ctx.plugin(...)`.
139
+
140
+ | Option | Default | Purpose |
141
+ | --- | --- | --- |
142
+ | `engines` | `startpage, duckduckgo, ecosia, google, mojeek` | engine ids tried in order; unlisted engines stay disabled |
143
+ | `timeoutMs` | `10000` | per-engine transport timeout; the chain worst case is `engines × timeoutMs` |
144
+ | `userAgent` | browser-shaped constant | User-Agent sent to the engines (deliberately not the product UA) |
145
+
146
+ ```yaml
147
+ - id: web-search-public
148
+ name: '@hy-sde-org/dsh-web-search-public'
149
+ config:
150
+ engines: [startpage, duckduckgo, google, mojeek] # drop Ecosia, reorder
151
+ timeoutMs: 8000
152
+ ```
153
+
154
+ `PublicEngineId` values are the exported engine ids; any id outside the known set is dropped
155
+ at startup, and a zero-engine config makes `available()` false (surfacing
156
+ `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`).
157
+
158
+ ## Errors
159
+
160
+ Search failures use the web seam’s `WEB_*` codes:
161
+
162
+ | Code | Meaning |
163
+ | --- | --- |
164
+ | `WEB_PROVIDER_ERROR` | every engine failed; message lists `id: reason; …` |
165
+ | `WEB_ABORTED` | caller aborted mid-search |
166
+ | `WEB_PROVIDER_CONFIGURED_UNAVAILABLE` | configured provider not available |
167
+ | `WEB_PROVIDER_CONFIGURED_MISSING` | configured provider id not registered |
168
+
169
+ ## Privacy and security notes
170
+
171
+ - **No credentials.** The provider never reads a key, so a leak cannot happen. Engines
172
+ receive the query, the browser-shaped User-Agent, and nothing else.
173
+ - **Treat queries as public.** Each query is sent to public search engines as an anonymous
174
+ request; do not search for secrets or PII you would not paste into a public search box.
175
+ - **No fetch provider.** `tool-web` `fetch: false` stays: the model cannot be pointed at
176
+ arbitrary URLs, so SSRF and unsafe-content surfaces stay closed.
177
+ - **Redirect policy.** All engine fetches use `redirect: "error"` — redirects are treated as
178
+ failures and advance the chain rather than leak the caller onto an external location.
179
+ - **Challenges are expected.** Startpage, Ecosia, and Google frequently serve bot checks;
180
+ the parsers treat challenged pages as zero results and the chain advances. See
181
+ [docs/operations.md](docs/operations.md) for runtime expectations.
182
+
183
+ ## Compatibility
184
+
185
+ | Component | Supported contract |
186
+ | --- | --- |
187
+ | Node.js | 22.19 or newer |
188
+ | DeepSeek Harness | `0.1.0-rc.6` (`@deepseek-ai/dsh-web`, `@deepseek-ai/cordis` peer range) |
189
+ | Seam | `ctx.web` `WebSearchProvider` (no key, no fetch provider) |
190
+
191
+ DeepSeek Harness is a developer preview. Upstream seam-contract changes require a new
192
+ package release and contract review.
193
+
194
+ ## Development
195
+
196
+ ```bash
197
+ npm ci
198
+ npm run check
199
+ npm test
200
+ npm run build
201
+ npm pack --dry-run
202
+ ```
203
+
204
+ The default suite runs parser and provider contract tests against fixture HTML (no network).
205
+ For live-network validation of the scrapers against current engine markup, run the opt-in
206
+ real test — it exercises Startpage, DuckDuckGo, and Mojeek for organic results and verifies
207
+ bot-challenged engines (Ecosia/Google) fail forward instead of crashing:
208
+
209
+ ```bash
210
+ DSH_WEB_SEARCH_REAL_E2E=1 npm run test:real
211
+ ```
212
+
213
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the release checklist and design constraints, and
214
+ [docs/operations.md](docs/operations.md) for runtime behavior and troubleshooting.
215
+
216
+ ## License and attribution
217
+
218
+ This package is licensed MIT — the same license as its upstream
219
+ [oh-my-pi](https://github.com/can1357/oh-my-pi). The credential-free engine chain and its
220
+ per-engine scrapers are ported from oh-my-pi (MIT License, © Mario Zechner 2025, © Can
221
+ Bölük 2025-2026); the upstream copyright holders are recorded in LICENSE next to this
222
+ package’s own notice, and the upstream notice text is reproduced in full in
223
+ [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).
224
+
225
+ This plugin is a separate installable package; the harness remains the property of its own
226
+ project.
package/SECURITY.md ADDED
@@ -0,0 +1,25 @@
1
+ # Security policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please use GitHub’s private security-advisory flow for this repository. Do not open a public
6
+ issue containing credentials, unpublished exploit details, or customer data.
7
+
8
+ Include the affected version, a minimal reproduction, impact, and any suggested mitigation.
9
+ Rotate any credential that may have been exposed during testing.
10
+
11
+ ## Deployment assumptions
12
+
13
+ - The plugin never reads, stores, or transmits credentials: searches are anonymous requests
14
+ to public search engines. There is nothing to leak.
15
+ - Queries are sent verbatim to the configured public engines, so treat a query as public
16
+ text. Do not search for secrets, tokens, or PII.
17
+ - No fetch provider is mounted (`tool-web` `fetch: false`), so the model cannot be pointed at
18
+ arbitrary URLs; SSRF surfaces stay closed.
19
+ - All engine fetches use `redirect: "error"`, so a hostile response cannot redirect the
20
+ provider onto an external location.
21
+ - Parsed snippets and titles are rendered to the model as citations; they are public
22
+ third-party text and are neither trusted nor executed.
23
+
24
+ See [docs/operations.md](docs/operations.md) for runtime expectations and troubleshooting.
25
+
@@ -0,0 +1,44 @@
1
+ # Third-party notices
2
+
3
+ This package includes code and design derived from
4
+ [oh-my-pi](https://github.com/can1357/oh-my-pi), specifically the credential-free engine
5
+ chain and per-engine scrapers originally at:
6
+
7
+ - `packages/coding-agent/src/web/search/providers/public.ts`
8
+ - `packages/coding-agent/src/web/search/providers/startpage.ts`
9
+ - `packages/coding-agent/src/web/search/providers/duckduckgo.ts`
10
+ - `packages/coding-agent/src/web/search/providers/ecosia.ts`
11
+ - `packages/coding-agent/src/web/search/providers/google.ts`
12
+ - `packages/coding-agent/src/web/search/providers/mojeek.ts`
13
+
14
+ The ported portions are adapted to the DeepSeek Harness `ctx.web` seam with sequential
15
+ fail-forward semantics (Startpage first, then DuckDuckGo → Ecosia → Google → Mojeek).
16
+
17
+ oh-my-pi is distributed under the following MIT License:
18
+
19
+ ```text
20
+ MIT License
21
+
22
+ Copyright (c) 2025 Mario Zechner
23
+ Copyright (c) 2025-2026 Can Bölük
24
+
25
+ Permission is hereby granted, free of charge, to any person obtaining a copy
26
+ of this software and associated documentation files (the "Software"), to deal
27
+ in the Software without restriction, including without limitation the rights
28
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ copies of the Software, and to permit persons to whom the Software is
30
+ furnished to do so, subject to the following conditions:
31
+
32
+ The above copyright notice and this permission notice shall be included in all
33
+ copies or substantial portions of the Software.
34
+
35
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41
+ SOFTWARE.
42
+ ```
43
+
44
+ All other code in this package is MIT (see LICENSE).
@@ -0,0 +1,12 @@
1
+ # Make the credential-free public search chain the default web-search provider:
2
+ # Startpage first, then DuckDuckGo -> Ecosia -> Google -> Mojeek as sequential
3
+ # fallbacks. The chain needs no API key and no fetch provider. The keyed DeepSeek
4
+ # route (web-search-deepseek) is left untouched, so deployments that explicitly
5
+ # choose their own provider via `web.searchProvider` keep that choice.
6
+ - id: web
7
+ config:
8
+ searchProvider: public
9
+
10
+ - insert:
11
+ - id: web-search-public
12
+ name: '@hy-sde-org/dsh-web-search-public'
@@ -0,0 +1,18 @@
1
+ /**
2
+ * DuckDuckGo engine: static HTML endpoint, no credentials, parseable without JS.
3
+ * @module @hy-sde-org/dsh-web-search-public/engines/duckduckgo
4
+ */
5
+ import type { WebSearchRequest, WebSearchSource } from '@deepseek-ai/dsh-web';
6
+ import type { PublicEngine, PublicEngineId } from '../types.ts';
7
+ /** A `WebSearchSource` may use `title`/optional fields; built via guarded assignment. */
8
+ export declare class DuckDuckGoEngine implements PublicEngine {
9
+ private readonly userAgent;
10
+ readonly id: PublicEngineId;
11
+ constructor(userAgent: string);
12
+ search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchSource[]>;
13
+ }
14
+ /** Unwrap DuckDuckGo's `uddg` redirect parameter; other http(s) hrefs pass through. */
15
+ export declare function unwrapDdgHref(href: string): string | undefined;
16
+ /** Parse DuckDuckGo's static HTML results page into sources. Exported for contract tests. */
17
+ export declare function parseDuckDuckGo(html: string, limit: number): WebSearchSource[];
18
+ //# sourceMappingURL=duckduckgo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duckduckgo.d.ts","sourceRoot":"","sources":["../../src/engines/duckduckgo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAM/D,yFAAyF;AACzF,qBAAa,gBAAiB,YAAW,YAAY;IAGvC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAFtC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAe;gBAEb,SAAS,EAAE,MAAM;IAExC,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAU1F;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO9D;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAY9E"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * DuckDuckGo engine: static HTML endpoint, no credentials, parseable without JS.
3
+ * @module @hy-sde-org/dsh-web-search-public/engines/duckduckgo
4
+ */
5
+ import { anchors, cleanText, elementText, elementsByClass, isUsableUrl, stripNoise } from "./html.js";
6
+ import { fetchHtml } from "./http.js";
7
+ const HTML_ENDPOINT = 'https://html.duckduckgo.com/html/';
8
+ /** A `WebSearchSource` may use `title`/optional fields; built via guarded assignment. */
9
+ export class DuckDuckGoEngine {
10
+ userAgent;
11
+ id = 'duckduckgo';
12
+ constructor(userAgent) {
13
+ this.userAgent = userAgent;
14
+ }
15
+ async search(request, signal) {
16
+ const html = await fetchHtml({
17
+ url: HTML_ENDPOINT,
18
+ method: 'POST',
19
+ body: new URLSearchParams({ q: request.query, kl: 'us-en' }).toString(),
20
+ userAgent: this.userAgent,
21
+ signal,
22
+ });
23
+ return parseDuckDuckGo(html, request.maxResults ?? 10);
24
+ }
25
+ }
26
+ /** Unwrap DuckDuckGo's `uddg` redirect parameter; other http(s) hrefs pass through. */
27
+ export function unwrapDdgHref(href) {
28
+ if (href.startsWith('//duckduckgo.com/l/?uddg=')) {
29
+ const target = new URL(href, 'https://duckduckgo.com').searchParams.get('uddg');
30
+ if (target === null)
31
+ return undefined;
32
+ return isUsableUrl(target) ? target : undefined;
33
+ }
34
+ return isUsableUrl(href) ? href : undefined;
35
+ }
36
+ /** Parse DuckDuckGo's static HTML results page into sources. Exported for contract tests. */
37
+ export function parseDuckDuckGo(html, limit) {
38
+ const doc = stripNoise(html);
39
+ const rows = elementsByClass(doc, 'result', 'div');
40
+ const sources = [];
41
+ for (let i = 0; i < rows.length && sources.length < limit; i += 1) {
42
+ const row = rows[i];
43
+ if (row === undefined)
44
+ continue;
45
+ const end = rows[i + 1]?.start ?? doc.length;
46
+ const source = parseDuckDuckGoRow(doc.slice(row.end, end));
47
+ if (source !== undefined)
48
+ sources.push(source);
49
+ }
50
+ return sources;
51
+ }
52
+ function parseDuckDuckGoRow(slice) {
53
+ const titleAnchor = anchors(slice).find(anchor => (anchor.attrs.class ?? '').split(/\s+/).includes('result__a'));
54
+ if (titleAnchor === undefined)
55
+ return undefined;
56
+ // Paid results arrive as `//duckduckgo.com/y.js?...` ad redirects carrying
57
+ // `ad_provider`/`ad_domain` query params; skip them, keeping only organic rows.
58
+ if (/^\/\/duckduckgo\.com\/y\.js\?/.test(titleAnchor.href)
59
+ || /[?&](ad_provider|ad_domain)=/.test(titleAnchor.href))
60
+ return undefined;
61
+ const url = unwrapDdgHref(titleAnchor.href);
62
+ if (url === undefined)
63
+ return undefined;
64
+ const title = cleanText(titleAnchor.text);
65
+ const snippetTag = elementsByClass(slice, 'result__snippet').find(tag => tag.tag === 'a' || tag.tag === 'div');
66
+ const snippet = snippetTag === undefined ? undefined : elementText(slice, snippetTag);
67
+ const dateTag = elementsByClass(slice, 'result__timestamp').at(0);
68
+ const date = dateTag === undefined ? undefined : elementText(slice, dateTag);
69
+ const iso = date === undefined ? undefined : /^\d{4}-\d{2}-\d{2}/.exec(date.trim())?.[0];
70
+ return {
71
+ url,
72
+ ...(title.length > 0 ? { title } : {}),
73
+ ...(snippet !== undefined && snippet.length > 0 ? { snippet } : {}),
74
+ ...(iso !== undefined ? { publishedAt: iso } : {}),
75
+ };
76
+ }
77
+ //# sourceMappingURL=duckduckgo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duckduckgo.js","sourceRoot":"","sources":["../../src/engines/duckduckgo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Ecosia engine: credential-free Google-indexed results served as static HTML
3
+ * (browser-backed in friction: may be slower or bot-challenged).
4
+ * @module @hy-sde-org/dsh-web-search-public/engines/ecosia
5
+ */
6
+ import type { WebSearchRequest, WebSearchSource } from '@deepseek-ai/dsh-web';
7
+ import type { PublicEngine, PublicEngineId } from '../types.ts';
8
+ export declare class EcosiaEngine implements PublicEngine {
9
+ private readonly userAgent;
10
+ readonly id: PublicEngineId;
11
+ constructor(userAgent: string);
12
+ search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchSource[]>;
13
+ }
14
+ /** Parse Ecosia's server-rendered result page. Exported for contract tests. */
15
+ export declare function parseEcosia(html: string, limit: number): WebSearchSource[];
16
+ //# sourceMappingURL=ecosia.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecosia.d.ts","sourceRoot":"","sources":["../../src/engines/ecosia.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAM/D,qBAAa,YAAa,YAAW,YAAY;IAGnC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAFtC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAW;gBAET,SAAS,EAAE,MAAM;IAExC,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAK1F;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAkB1E"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Ecosia engine: credential-free Google-indexed results served as static HTML
3
+ * (browser-backed in friction: may be slower or bot-challenged).
4
+ * @module @hy-sde-org/dsh-web-search-public/engines/ecosia
5
+ */
6
+ import { anchors, dedupeSources, elementText, elementsByClass, isUsableUrl, stripNoise } from "./html.js";
7
+ import { fetchHtml } from "./http.js";
8
+ const SEARCH_URL = 'https://www.ecosia.org/search';
9
+ export class EcosiaEngine {
10
+ userAgent;
11
+ id = 'ecosia';
12
+ constructor(userAgent) {
13
+ this.userAgent = userAgent;
14
+ }
15
+ async search(request, signal) {
16
+ const url = `${SEARCH_URL}?${new URLSearchParams({ q: request.query, method: 'index' })}`;
17
+ const html = await fetchHtml({ url, userAgent: this.userAgent, signal });
18
+ return parseEcosia(html, request.maxResults ?? 10);
19
+ }
20
+ }
21
+ /** Parse Ecosia's server-rendered result page. Exported for contract tests. */
22
+ export function parseEcosia(html, limit) {
23
+ const doc = stripNoise(html);
24
+ const links = anchors(doc)
25
+ .filter(anchor => isUsableUrl(anchor.href) && /(?:^|\s)result__a(?:$|\s)/.test(anchor.attrs.class ?? ''));
26
+ const snippetTags = elementsByClass(doc, 'result__quote');
27
+ const sources = [];
28
+ for (let i = 0; i < links.length && sources.length < limit; i += 1) {
29
+ const link = links[i];
30
+ if (link === undefined)
31
+ continue;
32
+ const snippetTag = snippetTags[i];
33
+ const snippet = snippetTag === undefined ? undefined : elementText(doc, snippetTag);
34
+ sources.push({
35
+ url: link.href,
36
+ ...(link.text.length > 0 ? { title: link.text } : {}),
37
+ ...(snippet !== undefined && snippet.length > 0 ? { snippet } : {}),
38
+ });
39
+ }
40
+ return dedupeSources(sources);
41
+ }
42
+ //# sourceMappingURL=ecosia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecosia.js","sourceRoot":"","sources":["../../src/engines/ecosia.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACzG,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,UAAU,GAAG,+BAA+B,CAAA;AAElD,MAAM,OAAO,YAAY;IAGM;IAFpB,EAAE,GAAmB,QAAQ,CAAA;IAEtC,YAA6B,SAAiB;QAAjB,cAAS,GAAT,SAAS,CAAQ;IAAG,CAAC;IAElD,KAAK,CAAC,MAAM,CAAC,OAAyB,EAAE,MAAoB;QAC1D,MAAM,GAAG,GAAG,GAAG,UAAU,IAAI,IAAI,eAAe,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAA;QACzF,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;QACxE,OAAO,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAA;IACpD,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAa;IACrD,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC;SACvB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAA;IAC3G,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;IACzD,MAAM,OAAO,GAAsB,EAAE,CAAA;IACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,IAAI,IAAI,KAAK,SAAS;YAAE,SAAQ;QAChC,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;QACjC,MAAM,OAAO,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC,CAAA;QACnF,OAAO,CAAC,IAAI,CAAC;YACX,GAAG,EAAE,IAAI,CAAC,IAAI;YACd,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpE,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,aAAa,CAAC,OAAO,CAAC,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Google engine: credential-free scrape of the classic results page. Google is
3
+ * the most bot-challenged engine in the chain, so this engine is expected to
4
+ * frequently advance the chain rather than succeed.
5
+ * @module @hy-sde-org/dsh-web-search-public/engines/google
6
+ */
7
+ import type { WebSearchRequest, WebSearchSource } from '@deepseek-ai/dsh-web';
8
+ import type { PublicEngine, PublicEngineId } from '../types.ts';
9
+ export declare class GoogleEngine implements PublicEngine {
10
+ private readonly userAgent;
11
+ readonly id: PublicEngineId;
12
+ constructor(userAgent: string);
13
+ search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchSource[]>;
14
+ }
15
+ /** Resolve Google's `/url?q=` redirect wrapper; other usable URLs pass through. Exported for tests. */
16
+ export declare function unwrapGoogleHref(href: string): string | undefined;
17
+ /** Parse Google's classic results page. Exported for contract tests. */
18
+ export declare function parseGoogle(html: string, limit: number): WebSearchSource[];
19
+ //# sourceMappingURL=google.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/engines/google.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAS/D,qBAAa,YAAa,YAAW,YAAY;IAGnC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAFtC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAW;gBAET,SAAS,EAAE,MAAM;IAExC,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAW1F;AAED,uGAAuG;AACvG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWjE;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAiC1E"}