Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ Search the web, get structured results, and extract page content as markdown —
## Quick Start

1. **Install the skill** (pick one method below)
2. **Set your API key**: `export QUERIT_API_KEY="your-key-here"`
2. **Set your API key** (choose one provider):
- Querit: `export QUERIT_API_KEY="your-key-here"`
- Tavily: `export TAVILY_API_KEY="tvly-your-key-here"`
3. **Use it**: Ask OpenClaw to "search the web for..." and it handles the rest

## Installation
Expand All @@ -31,17 +33,42 @@ Copy the skill files to `~/.openclaw/skills/querit-search/` and run `npm ci` in

## Configuration

Get a free API key at [querit.ai](https://querit.ai) (1,000 queries/month, no credit card required).
This skill supports two search providers. You only need one.

### Querit (default)

### Option A: Environment variable
Get a free API key at [querit.ai](https://querit.ai) (1,000 queries/month, no credit card required).

```bash
export QUERIT_API_KEY="your-key-here"
```

Add to your `~/.bashrc`, `~/.zshrc`, or `~/.profile` to persist.
### Tavily

Get a free API key at [app.tavily.com](https://app.tavily.com) (1,000 credits/month, no credit card required).

```bash
export TAVILY_API_KEY="tvly-your-key-here"
```

### Provider Selection

The provider is chosen automatically based on which API key is set. If both keys are present, Querit is used by default. Override with:

```bash
export SEARCH_PROVIDER="tavily" # or "querit"
```

**Tavily-specific notes:**
- Max 20 results per query (Querit supports up to 100)
- `--lang` filter is not supported (Querit-only); a warning is printed if used
- `--country` filter is supported
- `--date` filter maps to Tavily time ranges: `d1`→day, `w1`→week, `m1`→month, `y1`→year
- Content extraction (`content.js`) uses Tavily Extract with automatic fallback to the built-in jsdom pipeline

### Additional config methods

### Option B: OpenClaw config
#### OpenClaw config

In `~/.openclaw/openclaw.json`:

Expand All @@ -57,12 +84,13 @@ In `~/.openclaw/openclaw.json`:
}
```

### Option C: .env file
#### .env file

Create `~/.openclaw/.env`:

```
QUERIT_API_KEY=your-key-here
TAVILY_API_KEY=tvly-your-key-here
```

## Usage Examples
Expand Down
10 changes: 7 additions & 3 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: >-
Web search via Querit.ai API. Use when you need to search the web for
documentation, current events, facts, or any web content. Returns
structured results with titles, URLs, and snippets.
metadata: {"openclaw":{"emoji":"🔎","requires":{"env":["QUERIT_API_KEY"]},"primaryEnv":"QUERIT_API_KEY","install":[{"id":"node","kind":"node","label":"Install npm dependencies"}]}}
metadata: {"openclaw":{"emoji":"🔎","requires":{"env":[]},"optionalEnv":["QUERIT_API_KEY","TAVILY_API_KEY","SEARCH_PROVIDER"],"install":[{"id":"node","kind":"node","label":"Install npm dependencies"}]}}
---

# Querit Search
Expand All @@ -13,7 +13,11 @@ Web search and content extraction via the Querit.ai API. No browser required.

## Setup

Needs env: `QUERIT_API_KEY` — get a free key at https://querit.ai (1,000 queries/month).
Needs one of:
- `QUERIT_API_KEY` — get a free key at https://querit.ai (1,000 queries/month)
- `TAVILY_API_KEY` — get a free key at https://app.tavily.com (1,000 credits/month)

Optional: `SEARCH_PROVIDER` — set to `tavily` or `querit` to force a provider. If unset, auto-detects based on which API key is available.

## Search

Expand Down Expand Up @@ -87,7 +91,7 @@ Raw JSON array of result objects with fields: `url`, `title`, `snippet`, `page_a
## Limitations

- Query limited to 72 characters (auto-truncated with warning)
- Max 100 results per query
- Max 100 results per query (Querit); max 20 results per query (Tavily)
- Max 20 domains per site filter
- Free tier: 1,000 queries/month, 1 QPS
- Supported languages: english, japanese, korean, german, french, spanish, portuguese
53 changes: 51 additions & 2 deletions content.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ function usage() {
Fetches a URL and extracts the main content as readable markdown.

Environment:
No API key needed — fetches pages directly.`);
TAVILY_API_KEY Used if SEARCH_PROVIDER=tavily or only Tavily key is set
No API key needed for direct fetch (default).`);
}

function createTurndown() {
Expand Down Expand Up @@ -84,6 +85,54 @@ async function extractContent(url) {
return lines.join("\n");
}

/**
* Determine whether to use Tavily Extract based on SEARCH_PROVIDER / API key presence.
*/
function useTavily() {
const explicit = process.env.SEARCH_PROVIDER;
if (explicit) return explicit.toLowerCase() === "tavily";
return !!process.env.TAVILY_API_KEY && !process.env.QUERIT_API_KEY;
}

/**
* Extract content using Tavily Extract API, falling back to jsdom pipeline on failure.
*/
async function tavilyExtract(url) {
const { tavily } = await import("@tavily/core");
const client = tavily({ apiKey: process.env.TAVILY_API_KEY });
const response = await client.extract([url], { format: "markdown" });

if (!response.results || response.results.length === 0) {
throw new Error("Tavily Extract returned no results");
}

const result = response.results[0];
return result.raw_content || "(no content extracted)";
}

/**
* Top-level extraction dispatcher: tries Tavily if configured, falls back to jsdom.
*/
async function extractWithProvider(url) {
const explicit = process.env.SEARCH_PROVIDER;
if (explicit && explicit.toLowerCase() === "tavily" && !process.env.TAVILY_API_KEY) {
console.error(
"Warning: SEARCH_PROVIDER=tavily but TAVILY_API_KEY is not set; falling back to direct fetch"
);
}

if (useTavily() && process.env.TAVILY_API_KEY) {
try {
return await tavilyExtract(url);
} catch (err) {
console.error(
`Warning: Tavily Extract failed (${err.message}), falling back to direct fetch`
);
}
}
return extractContent(url);
}

async function main() {
const url = process.argv[2];

Expand All @@ -101,7 +150,7 @@ async function main() {
}

try {
const markdown = await extractContent(url);
const markdown = await extractWithProvider(url);
console.log(markdown);
} catch (err) {
console.error(`Error extracting content from ${url}: ${err.message}`);
Expand Down
Loading