@jzakirov/internet-search 0.2.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 +110 -0
- package/index.ts +80 -0
- package/openclaw.plugin.json +20 -0
- package/package.json +15 -0
- package/skills/internet-search/SKILL.md +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jamil Zakirov
|
|
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,110 @@
|
|
|
1
|
+
# internet-search
|
|
2
|
+
|
|
3
|
+
Web search for [OpenClaw](https://openclaw.dev) agents, backed by a self-hosted [SearXNG](https://searxng.github.io/searxng/) instance.
|
|
4
|
+
|
|
5
|
+
Registers a single `internet-search` tool with category routing for general, news, academic, and social results.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
openclaw plugins install @jzakirov/internet-search
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or from a local path (development):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
openclaw plugins install ./internet-search
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
Add to `openclaw.json` under `plugins.entries.internet-search.config`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"searxngUrl": "https://search.example.com/",
|
|
26
|
+
"searxngToken": "optional-bearer-token"
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Field | Required | Description |
|
|
31
|
+
|----------------|----------|-----------------------------------------------|
|
|
32
|
+
| `searxngUrl` | Yes | Base URL of your SearXNG instance |
|
|
33
|
+
| `searxngToken` | No | Token for SearXNG instances that require auth |
|
|
34
|
+
|
|
35
|
+
## Replacing the built-in web search
|
|
36
|
+
|
|
37
|
+
To use this plugin as the sole web search (disabling the default Brave/Perplexity `web_search` tool), set `tools.web.search.enabled` to `false`:
|
|
38
|
+
|
|
39
|
+
```json5
|
|
40
|
+
// openclaw.json
|
|
41
|
+
{
|
|
42
|
+
"tools": {
|
|
43
|
+
"web": {
|
|
44
|
+
"search": {
|
|
45
|
+
"enabled": false // disable built-in web_search tool
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"plugins": {
|
|
50
|
+
"allow": ["internet-search"],
|
|
51
|
+
"entries": {
|
|
52
|
+
"internet-search": {
|
|
53
|
+
"enabled": true,
|
|
54
|
+
"config": {
|
|
55
|
+
"searxngUrl": "https://search.example.com/",
|
|
56
|
+
"searxngToken": "your-token"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If you only want to add `internet-search` alongside the built-in tool (rather than replace it), omit the `tools.web.search.enabled` change.
|
|
65
|
+
|
|
66
|
+
## Tool: `internet-search`
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
internet-search(query, count?, category?)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| Parameter | Type | Default | Description |
|
|
73
|
+
|------------|--------|-----------|--------------------------------------------------|
|
|
74
|
+
| `query` | string | — | Search query |
|
|
75
|
+
| `count` | number | 5 | Number of results to return (1–20, capped at 10) |
|
|
76
|
+
| `category` | string | `general` | Routing category (see below) |
|
|
77
|
+
|
|
78
|
+
### Categories
|
|
79
|
+
|
|
80
|
+
| Value | Routes to |
|
|
81
|
+
|------------|--------------------------------|
|
|
82
|
+
| `general` | All configured SearXNG engines |
|
|
83
|
+
| `news` | SearXNG news category |
|
|
84
|
+
| `academic` | arXiv, Google Scholar, PubMed |
|
|
85
|
+
| `social` | Reddit |
|
|
86
|
+
|
|
87
|
+
### Response
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"results": [
|
|
92
|
+
{
|
|
93
|
+
"title": "Example result",
|
|
94
|
+
"url": "https://example.com/article",
|
|
95
|
+
"snippet": "A short excerpt from the page..."
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
HTML tags are stripped from snippets automatically.
|
|
102
|
+
|
|
103
|
+
## Requirements
|
|
104
|
+
|
|
105
|
+
- OpenClaw ≥ 2025.0.0
|
|
106
|
+
- A running SearXNG instance with JSON format enabled (`search.formats: [json]` in SearXNG config)
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
package/index.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// internet-search plugin: SearXNG-backed search tool
|
|
2
|
+
|
|
3
|
+
function stripHtml(input: string) {
|
|
4
|
+
return input
|
|
5
|
+
.replace(/<[^>]*>/g, " ")
|
|
6
|
+
.replace(/\s+/g, " ")
|
|
7
|
+
.trim();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Maps category → SearXNG query params.
|
|
11
|
+
// "general" uses all configured engines (no override).
|
|
12
|
+
// Others route to specialist engines for better signal.
|
|
13
|
+
const CATEGORY_ROUTING: Record<string, { categories?: string; engines?: string }> = {
|
|
14
|
+
general: {},
|
|
15
|
+
news: { categories: "news" },
|
|
16
|
+
academic: { engines: "arxiv,google scholar,pubmed" },
|
|
17
|
+
social: { engines: "reddit" },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default function (api: any) {
|
|
21
|
+
api.registerTool({
|
|
22
|
+
name: "internet-search",
|
|
23
|
+
description:
|
|
24
|
+
"Search the web using a SearXNG instance. Use this to gather news and learn new information. " +
|
|
25
|
+
"Use category='news' for recent events, 'academic' for research papers, 'social' for opinions/discussions.",
|
|
26
|
+
parameters: {
|
|
27
|
+
type: "object",
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
properties: {
|
|
30
|
+
query: { type: "string" },
|
|
31
|
+
count: { type: "number", minimum: 1, maximum: 20, default: 5 },
|
|
32
|
+
category: {
|
|
33
|
+
type: "string",
|
|
34
|
+
enum: ["general", "news", "academic", "social"],
|
|
35
|
+
default: "general",
|
|
36
|
+
description:
|
|
37
|
+
"general=broad web search (default); news=recent news & events; " +
|
|
38
|
+
"academic=arxiv/Scholar/PubMed; social=Reddit discussions"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
required: ["query"]
|
|
42
|
+
},
|
|
43
|
+
async execute(_id: string, params: any, signal: AbortSignal) {
|
|
44
|
+
const baseUrl = String(api.config?.searxngUrl ?? "").trim();
|
|
45
|
+
if (!baseUrl) {
|
|
46
|
+
throw new Error("searxngUrl is required in the internet-search plugin config");
|
|
47
|
+
}
|
|
48
|
+
const token = String(api.config?.searxngToken ?? "").trim();
|
|
49
|
+
|
|
50
|
+
const q = String(params.query ?? "").trim();
|
|
51
|
+
const count = Math.min(10, Math.max(1, Number(params.count ?? 5)));
|
|
52
|
+
const category = String(params.category ?? "general").trim();
|
|
53
|
+
const routing = CATEGORY_ROUTING[category] ?? {};
|
|
54
|
+
|
|
55
|
+
const url = new URL("/search", baseUrl);
|
|
56
|
+
url.searchParams.set("q", q);
|
|
57
|
+
url.searchParams.set("format", "json");
|
|
58
|
+
if (token) url.searchParams.set("token", token);
|
|
59
|
+
if (routing.categories) url.searchParams.set("categories", routing.categories);
|
|
60
|
+
if (routing.engines) url.searchParams.set("engines", routing.engines);
|
|
61
|
+
|
|
62
|
+
const res = await fetch(url.toString(), { signal });
|
|
63
|
+
if (!res.ok) throw new Error(`SearXNG HTTP ${res.status}`);
|
|
64
|
+
|
|
65
|
+
const data: any = await res.json();
|
|
66
|
+
const results = Array.isArray(data?.results) ? data.results : [];
|
|
67
|
+
const mapped = results.slice(0, count).map((r: any) => ({
|
|
68
|
+
title: String(r?.title ?? "").trim(),
|
|
69
|
+
url: String(r?.url ?? "").trim(),
|
|
70
|
+
snippet: stripHtml(String(r?.content ?? r?.snippet ?? ""))
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
content: [
|
|
75
|
+
{ type: "text", text: JSON.stringify({ results: mapped }, null, 2) }
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "internet-search",
|
|
3
|
+
"kind": "web-search",
|
|
4
|
+
"name": "Internet Search",
|
|
5
|
+
"description": "Web search via SearXNG. Registers the internet-search tool.",
|
|
6
|
+
"version": "0.2.0",
|
|
7
|
+
"configSchema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": false,
|
|
10
|
+
"properties": {
|
|
11
|
+
"searxngUrl": { "type": "string" },
|
|
12
|
+
"searxngToken": { "type": "string" }
|
|
13
|
+
},
|
|
14
|
+
"required": ["searxngUrl"]
|
|
15
|
+
},
|
|
16
|
+
"uiHints": {
|
|
17
|
+
"searxngUrl": { "label": "SearXNG URL", "placeholder": "https://search.example.com/" },
|
|
18
|
+
"searxngToken": { "label": "SearXNG Token", "sensitive": true }
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jzakirov/internet-search",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "SearXNG-backed internet search plugin for OpenClaw",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": ["openclaw", "openclaw-plugin", "searxng", "search", "internet-search", "web-search"],
|
|
8
|
+
"files": ["index.ts", "openclaw.plugin.json", "skills", "README.md", "LICENSE"],
|
|
9
|
+
"peerDependencies": {
|
|
10
|
+
"openclaw": ">=2025.0.0"
|
|
11
|
+
},
|
|
12
|
+
"openclaw": {
|
|
13
|
+
"extensions": ["./index.ts"]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: internet-search
|
|
3
|
+
description: "How to use the internet-search tool effectively — category routing, query formulation, and multi-search strategies. Use whenever web search is needed: current events, research papers, community opinions, or any information beyond training knowledge."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Internet Search
|
|
7
|
+
|
|
8
|
+
Queries a self-hosted SearXNG instance aggregating multiple search engines.
|
|
9
|
+
|
|
10
|
+
## Category Routing
|
|
11
|
+
|
|
12
|
+
Always set `category` based on the nature of the query.
|
|
13
|
+
|
|
14
|
+
| Category | When to use | Engines |
|
|
15
|
+
|------------|---------------------------------------------------------------------|--------------------------------------------------|
|
|
16
|
+
| `general` | Default. Facts, how-tos, products, people, broad web. | Brave, Bing, DDG, Startpage, Qwant, Wikipedia… |
|
|
17
|
+
| `news` | Recent events, breaking news, anything time-sensitive. | Bing News, DDG News |
|
|
18
|
+
| `academic` | Research papers, studies, medical literature, preprints. | arXiv, Google Scholar, PubMed |
|
|
19
|
+
| `social` | Opinions, community recommendations, "what do people think about X".| Reddit |
|
|
20
|
+
|
|
21
|
+
## Query Formulation
|
|
22
|
+
|
|
23
|
+
Write queries as a search engine expects — keywords, not full sentences:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
# Bad
|
|
27
|
+
"what is the fastest async runtime for rust"
|
|
28
|
+
|
|
29
|
+
# Good
|
|
30
|
+
"rust async runtime benchmarks 2025"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **news**: include a time anchor — `"OpenAI o3 release 2025"` not just `"OpenAI o3"`
|
|
34
|
+
- **academic**: use field terminology — `"transformer attention efficiency survey"`
|
|
35
|
+
- **social**: phrase as community search — `"reddit best mechanical keyboard 2025"`
|
|
36
|
+
|
|
37
|
+
## Count
|
|
38
|
+
|
|
39
|
+
- `count=5` (default) — sufficient for most tasks
|
|
40
|
+
- `count=10` — comparing many options, checking consensus
|
|
41
|
+
- `count=3` — quick fact checks
|
|
42
|
+
|
|
43
|
+
## Multi-Search Strategy
|
|
44
|
+
|
|
45
|
+
Fire multiple focused searches rather than one broad one:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
# Bad: one vague search
|
|
49
|
+
internet-search("best way to deploy Node.js")
|
|
50
|
+
|
|
51
|
+
# Good: three targeted searches
|
|
52
|
+
internet-search("Node.js Docker deployment best practices 2025")
|
|
53
|
+
internet-search("Node.js PM2 vs Docker production", category="social")
|
|
54
|
+
internet-search("Node.js zero-downtime deployment strategies")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Combine `general` + `social` for factual + sentiment coverage:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
internet-search("Bun runtime performance vs Node.js benchmarks")
|
|
61
|
+
internet-search("Bun runtime production experience", category="social")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## When NOT to Use
|
|
65
|
+
|
|
66
|
+
- Things you already know with high confidence
|
|
67
|
+
- Stable API docs or well-known syntax — use training knowledge
|
|
68
|
+
- Repeating a search that already answered the question
|
|
69
|
+
|
|
70
|
+
## Common Mistakes
|
|
71
|
+
|
|
72
|
+
| Mistake | Fix |
|
|
73
|
+
|-------------------------------------------|-----------------------------------------------|
|
|
74
|
+
| `general` for a research paper | Use `category="academic"` |
|
|
75
|
+
| Searching "what happened today" | Use `category="news"` with a specific topic |
|
|
76
|
+
| One broad search for a multi-part question| Break into 2–3 focused searches |
|
|
77
|
+
| Repeating a failed search verbatim | Rephrase with different keywords |
|
|
78
|
+
| `count=20` for a simple fact | Default `count=5` is almost always enough |
|