Skill: bartoszpijet/zai-search (version 1.0.0)

--- name: z.ai-web-search description: AI-optimized web search via Z.AI Web Search API. Returns structured results (title, URL, summary) for LLM processing. homepage: https://docs.z.ai/guides/tools/web-search metadata: {"clawdbot":{"emoji":"🔍","requires":{"bins":["node"],"env":["ZAI_API_KEY"]},"primaryEnv":"ZAI_API_KEY"}} --- # Z.AI Web Search AI-optimized web search using Z.AI Web Search API. Designed for LLMs – returns structured results with titles, URLs, summaries, site names and favicons. ## Search ```bash node {baseDir}/scripts/search.mjs "query" node {baseDir}/scripts/search.mjs "query" -n 15 node {baseDir}/scripts/search.mjs "query" --domain sohu.com node {baseDir}/scripts/search.mjs "query" --recency oneWeek node {baseDir}/scripts/search.mjs "query" --days 7 ``` ## Options - `-n <count>`: Number of results (default: 10, max: 50) - `--domain <domain>`: Limit results to specified domain (e.g. `sohu.com`, `www.example.com`) - `--recency <filter>`: Time range – `oneDay`, `oneWeek`, `oneMonth`, `oneYear`, `noLimit` (default) - `--days <n>`: Shorthand for recency (1→oneDay, 7→oneWeek, 30→oneMonth, 365→oneYear) ## Extract content from URL ```bash node {baseDir}/scripts/extract.mjs "https://example.com/article" ``` Note: Z.AI does not provide an extract API. This script uses native fetch to retrieve pages and strips HTML for basic text extraction. For richer content, use search results (they include summaries). ## Setup - Get API key at [Z.AI Platform](https://chat.z.ai) - Set `ZAI_API_KEY` in your environment - Docs: [Web Search Guide](https://docs.z.ai/guides/tools/web-search)

path: scripts/extract.mjs
content: #!/usr/bin/env node

/**
 * Extract text content from URLs.
 * Note: Z.AI does not provide an extract API. This script uses native fetch
 * to retrieve pages and strips HTML tags for basic text extraction.
 * For richer extraction, consider using z.ai Web Search results (content field).
 */

function usage() {
  console.error(`Usage: extract.mjs "url1" ["url2" ...]`);
  process.exit(2);
}

const args = process.argv.slice(2);
if (args.length === 0 || args[0] === "-h" || args[0] === "--help") usage();

const urls = args.filter(a => !a.startsWith("-"));

if (urls.length === 0) {
  console.error("No URLs provided");
  usage();
}

function stripHtml(html) {
  return html
    .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")
    .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "")
    .replace(/<[^>]+>/g, " ")
    .replace(/&nbsp;/g, " ")
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/\s+/g, " ")
    .trim();
}

async function fetchUrl(url) {
  const resp = await fetch(url, {
    headers: { "User-Agent": "Mozilla/5.0 (compatible; ZAI-Extract/1.0)" },
  });
  if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
  const html = await resp.text();
  return stripHtml(html);
}

for (const url of urls) {
  try {
    const content = await fetchUrl(url);
    console.log(`# ${url}\n`);
    console.log(content || "(no content extracted)");
    console.log("\n---\n");
  } catch (err) {
    console.log(`# ${url}\n`);
    console.log(`(failed: ${err.message})`);
    console.log("\n---\n");
  }
}


path: scripts/search.mjs
content: #!/usr/bin/env node

function usage() {
  console.error(`Usage: search.mjs "query" [-n 10] [--domain example.com] [--recency oneDay|oneWeek|oneMonth|oneYear|noLimit]`);
  process.exit(2);
}

const args = process.argv.slice(2);
if (args.length === 0 || args[0] === "-h" || args[0] === "--help") usage();

const query = args[0];
let n = 10;
let domainFilter = null;
let recencyFilter = "noLimit";

for (let i = 1; i < args.length; i++) {
  const a = args[i];
  if (a === "-n") {
    n = Number.parseInt(args[i + 1] ?? "10", 10);
    i++;
    continue;
  }
  if (a === "--domain") {
    domainFilter = args[i + 1] ?? null;
    i++;
    continue;
  }
  if (a === "--recency") {
    const val = args[i + 1] ?? "noLimit";
    if (["oneDay", "oneWeek", "oneMonth", "oneYear", "noLimit"].includes(val)) {
      recencyFilter = val;
    }
    i++;
    continue;
  }
  if (a === "--days") {
    const days = Number.parseInt(args[i + 1] ?? "7", 10);
    if (days <= 1) recencyFilter = "oneDay";
    else if (days <= 7) recencyFilter = "oneWeek";
    else if (days <= 30) recencyFilter = "oneMonth";
    else if (days <= 365) recencyFilter = "oneYear";
    else recencyFilter = "noLimit";
    i++;
    continue;
  }
  console.error(`Unknown arg: ${a}`);
  usage();
}

const [REDACTED_SECRET] ?? process.env.Z_AI_API_KEY ?? "").trim();
if (!apiKey) {
  console.error("Missing ZAI_API_KEY (get one at https://chat.z.ai)");
  process.exit(1);
}

const body = {
  search_engine: "search-prime",
  search_query: query,
  count: Math.max(1, Math.min(n, 50)),
  search_recency_filter: recencyFilter,
};

if (domainFilter) {
  body.search_domain_filter = domainFilter.startsWith("www.") ? domainFilter : `www.${domainFilter}`;
}

const resp = await fetch("https://api.z.ai/api/paas/v4/web_search", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${apiKey}`,
    "Accept-Language": "en-US,en",
  },
  body: JSON.stringify(body),
});

if (!resp.ok) {
  const text = await resp.text().catch(() => "");
  throw new Error(`Z.AI Web Search failed (${resp.status}): ${text}`);
}

const data = await resp.json();

const results = data.search_result ?? [];
console.log("## Sources\n");

for (const r of results) {
  const title = String(r?.title ?? "").trim();
  const url = String(r?.link ?? "").trim();
  const content = String(r?.content ?? "").trim();
  const media = String(r?.media ?? "").trim();
  const publishDate = String(r?.publish_date ?? "").trim();

  if (!title || !url) continue;

  const meta = [media, publishDate].filter(Boolean).join(" • ");
  console.log(`- **${title}**${meta ? ` (${meta})` : ""}`);
  console.log(`  ${url}`);
  if (content) {
    console.log(`  ${content.slice(0, 400)}${content.length > 400 ? "..." : ""}`);
  }
  console.log();
}


path: README.md
content: # Z.AI Web Search

Web search skill using [Z.AI Web Search API](https://docs.z.ai/guides/tools/web-search). Returns structured results (title, URL, summary, site name) optimized for LLM processing.

## Setup

1. Get an API key from [Z.AI Platform](https://chat.z.ai)
2. Set environment variable: `export ZAI_API_KEY="your-api-key"`

## Usage

### Search

```bash
node scripts/search.mjs "your search query"
node scripts/search.mjs "economic news" -n 15
node scripts/search.mjs "news" --domain sohu.com --days 7
```

### Extract (basic)

```bash
node scripts/extract.mjs "https://example.com/article"
```

Note: Z.AI has no extract API. The extract script uses native fetch + HTML stripping as a fallback.

## Options

| Option | Description |
|--------|-------------|
| `-n <count>` | Number of results (1–50, default 10) |
| `--domain <domain>` | Limit to domain (e.g. `sohu.com`) |
| `--recency <filter>` | `oneDay`, `oneWeek`, `oneMonth`, `oneYear`, `noLimit` |
| `--days <n>` | Shorthand for recency |

## API Reference

- [Web Search Guide](https://docs.z.ai/guides/tools/web-search)
- [Web Search API](https://docs.z.ai/api-reference/tools/web-search)


Security scan verdicts — ClawScan: clean | static analysis: suspicious | VirusTotal: suspicious | SkillSpector:  ()