mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
feat(plugins): externalize DuckDuckGo search (#116740)
This commit is contained in:
@@ -51,7 +51,7 @@ Each entry lists the package, distribution route, and description.
|
||||
|
||||
## Core npm package
|
||||
|
||||
66 plugins
|
||||
65 plugins
|
||||
|
||||
- **[admin-http-rpc](/plugins/reference/admin-http-rpc)** (`@openclaw/admin-http-rpc`) - included in OpenClaw. OpenClaw admin HTTP RPC endpoint.
|
||||
|
||||
@@ -85,8 +85,6 @@ Each entry lists the package, distribution route, and description.
|
||||
|
||||
- **[document-extract](/plugins/reference/document-extract)** (`@openclaw/document-extract-plugin`) - included in OpenClaw. Extract text and fallback page images from local document attachments.
|
||||
|
||||
- **[duckduckgo](/plugins/reference/duckduckgo)** (`@openclaw/duckduckgo-plugin`) - included in OpenClaw. Adds web search provider support.
|
||||
|
||||
- **[elevenlabs](/plugins/reference/elevenlabs)** (`@openclaw/elevenlabs-speech`) - included in OpenClaw. Adds media understanding provider support. Adds realtime transcription provider support. Adds text-to-speech provider support.
|
||||
|
||||
- **[fal](/plugins/reference/fal)** (`@openclaw/fal-provider`) - included in OpenClaw. Adds fal model provider support to OpenClaw.
|
||||
@@ -187,7 +185,7 @@ Each entry lists the package, distribution route, and description.
|
||||
|
||||
## Official external packages
|
||||
|
||||
79 plugins
|
||||
80 plugins
|
||||
|
||||
- **[acpx](/plugins/reference/acpx)** (`@openclaw/acpx`) - npm; ClawHub. OpenClaw ACP runtime backend with plugin-owned session and transport management.
|
||||
|
||||
@@ -233,6 +231,8 @@ Each entry lists the package, distribution route, and description.
|
||||
|
||||
- **[discord](/plugins/reference/discord)** (`@openclaw/discord`) - npm; ClawHub. OpenClaw Discord channel plugin for channels, DMs, commands, and app events.
|
||||
|
||||
- **[duckduckgo](/plugins/reference/duckduckgo)** (`@openclaw/duckduckgo-plugin`) - npm; ClawHub: `clawhub:@openclaw/duckduckgo-plugin`. Adds web search provider support.
|
||||
|
||||
- **[exa](/plugins/reference/exa)** (`@openclaw/exa-plugin`) - npm; ClawHub: `clawhub:@openclaw/exa-plugin`. Adds web search provider support.
|
||||
|
||||
- **[featherless](/plugins/reference/featherless)** (`@openclaw/featherless-provider`) - npm; ClawHub: `clawhub:@openclaw/featherless-provider`. OpenClaw Featherless AI provider plugin.
|
||||
|
||||
@@ -12,7 +12,7 @@ Adds web search provider support.
|
||||
## Distribution
|
||||
|
||||
- Package: `@openclaw/duckduckgo-plugin`
|
||||
- Install route: included in OpenClaw
|
||||
- Install route: npm; ClawHub: `clawhub:@openclaw/duckduckgo-plugin`
|
||||
|
||||
## Surface
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ OpenClaw supports DuckDuckGo as a **key-free** `web_search` provider. No API key
|
||||
DuckDuckGo is never auto-selected, since auto-detection only considers providers with usable credentials. Set it explicitly:
|
||||
|
||||
<Steps>
|
||||
<Step title="Install the plugin">
|
||||
```bash
|
||||
openclaw plugins install @openclaw/duckduckgo-plugin
|
||||
openclaw gateway restart
|
||||
```
|
||||
</Step>
|
||||
<Step title="Configure">
|
||||
```bash
|
||||
openclaw configure --section web
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# OpenClaw DuckDuckGo Plugin
|
||||
|
||||
Official OpenClaw plugin for DuckDuckGo web search.
|
||||
|
||||
Install from OpenClaw:
|
||||
|
||||
```bash
|
||||
openclaw plugins install @openclaw/duckduckgo-plugin
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
See <https://docs.openclaw.ai/tools/duckduckgo-search> for setup and configuration.
|
||||
@@ -5,7 +5,7 @@ import { createDuckDuckGoWebSearchProvider } from "./src/ddg-search-provider.js"
|
||||
export default definePluginEntry({
|
||||
id: "duckduckgo",
|
||||
name: "DuckDuckGo Plugin",
|
||||
description: "Bundled DuckDuckGo web search plugin",
|
||||
description: "Official DuckDuckGo web search plugin",
|
||||
register(api) {
|
||||
api.registerWebSearchProvider(createDuckDuckGoWebSearchProvider());
|
||||
},
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
{
|
||||
"name": "@openclaw/duckduckgo-plugin",
|
||||
"version": "2026.7.2",
|
||||
"private": true,
|
||||
"description": "OpenClaw DuckDuckGo plugin",
|
||||
"description": "OpenClaw DuckDuckGo plugin.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openclaw/openclaw"
|
||||
},
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@openclaw/plugin-sdk": "workspace:*"
|
||||
@@ -10,6 +13,23 @@
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
],
|
||||
"install": {
|
||||
"clawhubSpec": "clawhub:@openclaw/duckduckgo-plugin",
|
||||
"npmSpec": "@openclaw/duckduckgo-plugin",
|
||||
"defaultChoice": "npm",
|
||||
"minHostVersion": ">=2026.7.2"
|
||||
},
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.7.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.7.2",
|
||||
"bundledDist": false
|
||||
},
|
||||
"release": {
|
||||
"publishToClawHub": true,
|
||||
"publishToNpm": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,7 @@
|
||||
"!dist/extensions/diffs/**",
|
||||
"!dist/extensions/diffs-language-pack/**",
|
||||
"!dist/extensions/discord/**",
|
||||
"!dist/extensions/duckduckgo/**",
|
||||
"!dist/extensions/exa/**",
|
||||
"!dist/extensions/feishu/**",
|
||||
"!dist/extensions/featherless/**",
|
||||
|
||||
@@ -169,6 +169,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "@openclaw/duckduckgo-plugin",
|
||||
"description": "OpenClaw DuckDuckGo plugin.",
|
||||
"source": "official",
|
||||
"kind": "plugin",
|
||||
"openclaw": {
|
||||
"plugin": {
|
||||
"id": "duckduckgo",
|
||||
"label": "DuckDuckGo"
|
||||
},
|
||||
"contracts": {
|
||||
"webSearchProviders": [
|
||||
"duckduckgo"
|
||||
]
|
||||
},
|
||||
"webSearchProviders": [
|
||||
{
|
||||
"id": "duckduckgo",
|
||||
"label": "DuckDuckGo Search (experimental)",
|
||||
"hint": "Free web search fallback with no API key required",
|
||||
"onboardingScopes": [
|
||||
"text-inference"
|
||||
],
|
||||
"requiresCredential": false,
|
||||
"envVars": [],
|
||||
"placeholder": "(no key needed)",
|
||||
"signupUrl": "https://duckduckgo.com/",
|
||||
"docsUrl": "https://docs.openclaw.ai/tools/duckduckgo-search",
|
||||
"credentialPath": "",
|
||||
"autoDetectOrder": 100
|
||||
}
|
||||
],
|
||||
"install": {
|
||||
"clawhubSpec": "clawhub:@openclaw/duckduckgo-plugin",
|
||||
"npmSpec": "@openclaw/duckduckgo-plugin",
|
||||
"defaultChoice": "npm",
|
||||
"minHostVersion": ">=2026.7.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "@openclaw/exa-plugin",
|
||||
"description": "OpenClaw Exa plugin.",
|
||||
|
||||
@@ -165,12 +165,13 @@ describe("listPersistedBundledPluginLocationBridges", () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
["synthetic", "@openclaw/synthetic-provider"],
|
||||
["teams-meetings", "@openclaw/teams-meetings"],
|
||||
["zoom-meetings", "@openclaw/zoom-meetings"],
|
||||
])(
|
||||
"externalizes the shipped bundled %s plugin while preserving default enablement",
|
||||
async (pluginId, npmSpec) => {
|
||||
["duckduckgo", "@openclaw/duckduckgo-plugin", false],
|
||||
["synthetic", "@openclaw/synthetic-provider", true],
|
||||
["teams-meetings", "@openclaw/teams-meetings", true],
|
||||
["zoom-meetings", "@openclaw/zoom-meetings", true],
|
||||
] as const)(
|
||||
"externalizes the shipped bundled %s plugin using official install metadata",
|
||||
async (pluginId, npmSpec, enabledByDefault) => {
|
||||
readPersistedInstalledPluginIndexMock.mockResolvedValue(
|
||||
makeIndex({
|
||||
pluginId,
|
||||
@@ -180,7 +181,7 @@ describe("listPersistedBundledPluginLocationBridges", () => {
|
||||
rootDir: `/app/dist/extensions/${pluginId}`,
|
||||
origin: "bundled",
|
||||
enabled: true,
|
||||
enabledByDefault: true,
|
||||
...(enabledByDefault ? { enabledByDefault: true } : {}),
|
||||
startup: startupInfo,
|
||||
compat: [],
|
||||
packageInstall: {
|
||||
@@ -197,7 +198,7 @@ describe("listPersistedBundledPluginLocationBridges", () => {
|
||||
preferredSource: "npm",
|
||||
npmSpec,
|
||||
clawhubSpec: `clawhub:${npmSpec}`,
|
||||
enabledByDefault: true,
|
||||
...(enabledByDefault ? { enabledByDefault: true } : {}),
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
@@ -1972,6 +1972,34 @@ describe("official external plugin catalog", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves DuckDuckGo's keyless web search setup contract", () => {
|
||||
const duckduckgo = expectCatalogEntry("duckduckgo");
|
||||
const manifest = getOfficialExternalPluginCatalogManifest(duckduckgo);
|
||||
|
||||
expect(resolveOfficialExternalPluginInstall(duckduckgo)).toEqual({
|
||||
clawhubSpec: "clawhub:@openclaw/duckduckgo-plugin",
|
||||
npmSpec: "@openclaw/duckduckgo-plugin",
|
||||
defaultChoice: "npm",
|
||||
minHostVersion: ">=2026.7.2",
|
||||
});
|
||||
expect(manifest?.contracts?.webSearchProviders).toEqual(["duckduckgo"]);
|
||||
expect(manifest?.webSearchProviders).toEqual([
|
||||
{
|
||||
id: "duckduckgo",
|
||||
label: "DuckDuckGo Search (experimental)",
|
||||
hint: "Free web search fallback with no API key required",
|
||||
onboardingScopes: ["text-inference"],
|
||||
requiresCredential: false,
|
||||
envVars: [],
|
||||
placeholder: "(no key needed)",
|
||||
signupUrl: "https://duckduckgo.com/",
|
||||
docsUrl: "https://docs.openclaw.ai/tools/duckduckgo-search",
|
||||
credentialPath: "",
|
||||
autoDetectOrder: 100,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["teams-meetings", "@openclaw/teams-meetings", "teams_meetings", "teams"],
|
||||
["zoom-meetings", "@openclaw/zoom-meetings", "zoom_meetings", "zoom"],
|
||||
|
||||
@@ -367,6 +367,14 @@ describe("bundled plugin build entries", () => {
|
||||
expectNoPrefixMatches(artifacts, "dist/extensions/synthetic/");
|
||||
});
|
||||
|
||||
it("excludes the externalized DuckDuckGo plugin from bundled artifacts", () => {
|
||||
const artifacts = listBundledPluginPackArtifacts();
|
||||
|
||||
expect(artifacts).not.toContain("dist/extensions/duckduckgo/index.js");
|
||||
expect(artifacts).not.toContain("dist/extensions/duckduckgo/openclaw.plugin.json");
|
||||
expect(artifacts).not.toContain("dist/extensions/duckduckgo/package.json");
|
||||
});
|
||||
|
||||
it("keeps bundled channel secret contracts on packed top-level sidecars", () => {
|
||||
const artifacts = listBundledPluginPackArtifacts();
|
||||
const excludedPackageDirs = collectRootPackageExcludedExtensionDirs();
|
||||
|
||||
Reference in New Issue
Block a user