web-search-plus-plugin 1.3.1 → 1.3.3
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/index.ts +64 -39
- package/openclaw.plugin.json +2 -1
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
|
-
import {
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
4
|
import fs from "fs";
|
|
5
5
|
import path from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
@@ -15,6 +15,12 @@ function getPluginDir(): string {
|
|
|
15
15
|
return path.join(process.cwd(), "skills", "web-search-plus-plugin");
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const SENSITIVE_PATTERN = /(?:key|token|secret|password|api[_-]?key)\s*[=:]\s*\S+/gi;
|
|
19
|
+
|
|
20
|
+
function sanitizeOutput(text: string): string {
|
|
21
|
+
return text.replace(SENSITIVE_PATTERN, "[REDACTED]");
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
function loadEnvFile(envPath: string): Record<string, string> {
|
|
19
25
|
if (!fs.existsSync(envPath)) return {};
|
|
20
26
|
const env: Record<string, string> = {};
|
|
@@ -32,6 +38,48 @@ function loadEnvFile(envPath: string): Record<string, string> {
|
|
|
32
38
|
return env;
|
|
33
39
|
}
|
|
34
40
|
|
|
41
|
+
function runPython(
|
|
42
|
+
args: string[],
|
|
43
|
+
env: NodeJS.ProcessEnv,
|
|
44
|
+
timeoutMs: number,
|
|
45
|
+
): Promise<{ stdout: string; stderr: string; code: number }> {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const child = spawn("python3", args, { env, shell: false });
|
|
48
|
+
let stdout = "";
|
|
49
|
+
let stderr = "";
|
|
50
|
+
let settled = false;
|
|
51
|
+
|
|
52
|
+
const timer = setTimeout(() => {
|
|
53
|
+
if (!settled) {
|
|
54
|
+
settled = true;
|
|
55
|
+
child.kill();
|
|
56
|
+
resolve({ stdout: "", stderr: "Search timed out", code: 1 });
|
|
57
|
+
}
|
|
58
|
+
}, timeoutMs);
|
|
59
|
+
|
|
60
|
+
child.stdout.on("data", (d: Buffer) => { stdout += d.toString(); });
|
|
61
|
+
child.stderr.on("data", (d: Buffer) => { stderr += d.toString(); });
|
|
62
|
+
|
|
63
|
+
child.on("close", (code: number | null) => {
|
|
64
|
+
if (!settled) {
|
|
65
|
+
settled = true;
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
resolve({ stdout, stderr, code: code ?? 1 });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
child.on("error", (err: Error) => {
|
|
72
|
+
if (!settled) {
|
|
73
|
+
settled = true;
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
// Only expose the error message, not the full error object (may contain env/args)
|
|
76
|
+
const safeMsg = err.code === "ENOENT" ? "python3 not found" : "Process error";
|
|
77
|
+
resolve({ stdout: "", stderr: safeMsg, code: 1 });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
35
83
|
const PLUGIN_DIR = getPluginDir();
|
|
36
84
|
const scriptPath = path.join(PLUGIN_DIR, "scripts", "search.py");
|
|
37
85
|
|
|
@@ -139,6 +187,12 @@ export default definePluginEntry({
|
|
|
139
187
|
exclude_domains?: string[];
|
|
140
188
|
},
|
|
141
189
|
) {
|
|
190
|
+
if (!fs.existsSync(scriptPath)) {
|
|
191
|
+
return {
|
|
192
|
+
content: [{ type: "text", text: `Search failed: script not found at ${scriptPath}` }],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
142
196
|
const args = [scriptPath, "--query", params.query, "--compact"];
|
|
143
197
|
|
|
144
198
|
if (params.provider && params.provider !== "auto") {
|
|
@@ -146,10 +200,7 @@ export default definePluginEntry({
|
|
|
146
200
|
}
|
|
147
201
|
|
|
148
202
|
if (typeof params.count === "number" && Number.isFinite(params.count)) {
|
|
149
|
-
args.push(
|
|
150
|
-
"--max-results",
|
|
151
|
-
String(Math.max(1, Math.floor(params.count))),
|
|
152
|
-
);
|
|
203
|
+
args.push("--max-results", String(Math.max(1, Math.floor(params.count))));
|
|
153
204
|
}
|
|
154
205
|
|
|
155
206
|
if (params.depth && params.depth !== "normal") {
|
|
@@ -179,44 +230,18 @@ export default definePluginEntry({
|
|
|
179
230
|
}
|
|
180
231
|
const childEnv = { ...process.env, ...configEnv, ...fileEnv };
|
|
181
232
|
|
|
182
|
-
|
|
183
|
-
const child = spawnSync("python3", args, {
|
|
184
|
-
timeout: 75000,
|
|
185
|
-
env: childEnv,
|
|
186
|
-
shell: false,
|
|
187
|
-
encoding: "utf8",
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
if (child.error) {
|
|
191
|
-
return {
|
|
192
|
-
content: [
|
|
193
|
-
{ type: "text", text: `Search failed: ${child.error.message}` },
|
|
194
|
-
],
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (child.status !== 0) {
|
|
199
|
-
const stderr = child.stderr?.trim() || "Unknown error";
|
|
200
|
-
return {
|
|
201
|
-
content: [
|
|
202
|
-
{
|
|
203
|
-
type: "text",
|
|
204
|
-
text: `Search failed (exit ${child.status}): ${stderr}`,
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
};
|
|
208
|
-
}
|
|
233
|
+
const result = await runPython(args, childEnv, 75000);
|
|
209
234
|
|
|
235
|
+
if (result.code !== 0) {
|
|
236
|
+
const stderr = sanitizeOutput(result.stderr.trim()) || "Unknown error";
|
|
210
237
|
return {
|
|
211
|
-
content: [{ type: "text", text:
|
|
212
|
-
};
|
|
213
|
-
} catch (err: any) {
|
|
214
|
-
return {
|
|
215
|
-
content: [
|
|
216
|
-
{ type: "text", text: `Search failed: ${err?.message ?? err}` },
|
|
217
|
-
],
|
|
238
|
+
content: [{ type: "text", text: `Search failed (exit ${result.code}): ${stderr}` }],
|
|
218
239
|
};
|
|
219
240
|
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
content: [{ type: "text", text: sanitizeOutput(result.stdout.trim()) || "{}" }],
|
|
244
|
+
};
|
|
220
245
|
},
|
|
221
246
|
},
|
|
222
247
|
{ optional: true },
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "web-search-plus-plugin",
|
|
3
3
|
"name": "Web Search Plus",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.3",
|
|
5
5
|
"description": "Multi-provider web search (Serper/Google, Tavily, Querit/Multilingual AI Search, Exa/Neural+Deep, Perplexity, You.com, SearXNG) with intelligent auto-routing",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"serperApiKey": { "label": "Serper API Key", "placeholder": "sk-...", "sensitive": true },
|
|
22
22
|
"tavilyApiKey": { "label": "Tavily API Key", "placeholder": "tvly-...", "sensitive": true },
|
|
23
23
|
"queritApiKey": { "label": "Querit API Key", "placeholder": "querit-sk-...", "sensitive": true },
|
|
24
|
+
"queritApiKey": { "label": "Querit API Key", "placeholder": "querit-sk-...", "sensitive": true },
|
|
24
25
|
"exaApiKey": { "label": "Exa API Key", "placeholder": "exa-...", "sensitive": true },
|
|
25
26
|
"perplexityApiKey": { "label": "Perplexity API Key", "placeholder": "pplx-...", "sensitive": true },
|
|
26
27
|
"kilocodeApiKey": { "label": "Kilo Gateway API Key", "placeholder": "...", "sensitive": true },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "web-search-plus-plugin",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "OpenClaw plugin: multi-provider web search (Serper/Google, Tavily, Querit/Multilingual AI Search, Exa/Neural+Deep, Perplexity, You.com, SearXNG) with intelligent auto-routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|