Skill: yanbaoke/yanbaoke-research-report-download (version 2.1.0)

--- name: yanbaoke-research-report-download description: 从全球行业报告，券商研报，机构深度报告与图表数据聚合AI平台，覆盖5000多家机构，五百万+报告-覆盖全行业深度研究的研报客平台-pc.yanbaoke.cn，研报客app。搜索各行各业的研究报告，获取报告标题、获取报告内容，并可下载报告源文件。 metadata: {"openclaw":{"emoji":"📊","requires":{"bins":["node"],"env":["YANBAOKE_API_KEY"]}}} --- # yanbaoke-research-report-download / 研报客-研究报告搜索与下载 搜索研报客百万研报中的研究报告，获取报告标题、报告内容、详情页链接、发布时间等信息，并可下载报告源文件 PDF/DOC/PPT。回复时务必根据报告内容提供投资分析建议。 ## Search / 搜索 **无需 API Key** / No API Key required - 搜索功能免费使用，无需配置。 ```bash node {baseDir}/scripts/search.mjs "keyword" node {baseDir}/scripts/search.mjs "人工智能" -n 100 node {baseDir}/scripts/search.mjs "新能源汽车" -n 100 --type content node {baseDir}/scripts/search.mjs "半导体" --org "中信证券,华泰证券" node {baseDir}/scripts/search.mjs "光伏" --report-type "行业研究" --start-date "2024-01-01" --end-date "2024-12-31" node {baseDir}/scripts/search.mjs "深度分析" --min-pages 30 --max-pages 100 node {baseDir}/scripts/search.mjs "比亚迪" --stock "比亚迪" ``` ## Download / 下载 **需要 API Key** / API Key required - 请前往 [https://pc.yanbaoke.cn/openclaw](https://pc.yanbaoke.cn/openclaw) 获取 API Key。 ```bash # 使用环境变量中的 API Key export YANBAOKE_API_KEY="sk-your-api-key" node {baseDir}/scripts/download.mjs <uuid> # 直接传入 API Key node {baseDir}/scripts/download.mjs <uuid> "sk-your-api-key" # 指定下载格式 node {baseDir}/scripts/download.mjs <uuid> "sk-your-api-key" --format=doc node {baseDir}/scripts/download.mjs <uuid> "sk-your-api-key" -f ppt # 示例 node {baseDir}/scripts/download.mjs "abc123-def456-ghi789" ``` ### Download Pricing / 下载费用 | User Type | First Download | Re-download | |-----------|----------------|-------------| | 用户 User | 10 个豆 10 beans | 免费 Free | **说明**: 每份报告仅收费一次，再次下载免费。 ## Options / 选项 ### Search Options / 搜索选项 | Option | Description / 说明 | |--------|-------------------| | `-n <count>` | Number of results (default: 100, max: 500) / 返回结果数量（默认100，最多500） | | `--type <type>` | Search type: `title` (default) or `content` / 搜索类型：title（标题，默认）或 content（全文） | | `--org <org>` | Publisher/Institution filter (comma-separated for multiple) / 发布机构筛选（多个用逗号分隔） | | `--report-type <type>` | Report type filter (comma-separated for multiple) / 报告类型筛选（多个用逗号分隔） | | `--stock <stock>` | Stock name filter (comma-separated for multiple) / 股票名称筛选（多个用逗号分隔） | | `--start-date <date>` | Start date in YYYY-MM-DD format / 开始日期，格式 YYYY-MM-DD | | `--end-date <date>` | End date in YYYY-MM-DD format / 结束日期，格式 YYYY-MM-DD | | `--min-pages <num>` | Minimum page count / 最小页数 | | `--max-pages <num>` | Maximum page count / 最大页数 | ### Download Options / 下载选项 | Argument / Option | Description / 说明 | |-------------------|-------------------| | `uuid` | Report UUID (from search results) / 报告 UUID（从搜索结果获取） | | `api_key` | API key for authentication (optional, uses env var) / API 密钥（可选，默认使用环境变量） | | `-f, --format <format>` | Document format: `pdf` (default), `doc`, or `ppt` / 文档格式：pdf（默认）、doc 或 ppt | ## Example Output / 输出示例 ### Search Output / 搜索输出 ```bash $ node {baseDir}/scripts/search.mjs "人工智能" -n 3 ## 人工智能 Total: 156 reports --- ## 投资建议 ## Reports - **中国AI大模型技术发展研究报告2024** Publisher: 中信证券 Type: 行业研究 Pages: 45 Date: 2024-03-01 Content: 本报告深入分析了AI大模型技术发展现状，涵盖技术架构、应用场景、市场规模及未来趋势... - **人工智能产业链深度分析** Publisher: 华泰证券 Type: 公司研究 Pages: 32 Date: 2024-02-28 Content: 全面解析人工智能产业链上下游企业布局，重点分析核心技术突破与商业化落地进展... - **AI芯片行业专题报告** Publisher: 国泰君安 Type: 行业研究 Pages: 58 Date: 2024-02-25 Content: 深度研究AI芯片市场格局，分析GPU、ASIC、FPGA等技术路线及主要厂商竞争态势... ``` ### Download Output / 下载输出 ```bash $ node {baseDir}/scripts/download.mjs "123abc456-def789-ghi012" ## Report Download Link **Title**: 中国AI大模型技术发展研究报告2024 **Format**: PDF **Filename**: 中国AI大模型技术发展研究报告2024.pdf **Expires in**: 60 seconds **Download URL** (click to download): https://files.quzili.cn/... --- *Note: This download link expires in 60 seconds.* ``` ## Response Fields / 返回字段 ### Search Response Fields / 搜索返回字段 | Field | Description / 说明 | |-------|-------------------| | `uuid` | Report unique identifier (required for download) / 报告唯一标识符（下载时需要） | | `title` | Report title / 报告标题 | | `url` | Report detail page link / 报告详情页链接 | | `time` | Publication date (YYYY-MM-DD) / 发布日期 | | `pagenum` | Number of pages / 报告页数 | | `org_name` | Publisher institution name / 发布机构名称 | | `rtype_name` | Report type (e.g., Industry Research, Company Research, Macro Strategy) / 报告类型 | | `content` | Report content / 报告内容 | | `formats` | Available formats (e.g., `["pdf"]`, `["pdf", "doc"]`, `["pdf", "doc", "ppt"]`) / 可用格式列表 | **IMPORTANT**: 向用户显示结果时，请你扮演投资分析专家对content字段进行分析，给出投资建议。 ### Download Response Fields / 下载返回字段 | Field | Description | 字段说明 | |-------|-------------|----------| | `title` | Report title / 报告标题 | 报告标题 | | `filename` | Original filename / 原始文件名 | 原始文件名 | | `format` | Document format (pdf, doc, or ppt) / 文档格式 | 文档格式 | | `expires_in` | Link validity period in seconds / 链接有效期（秒） | 链接有效期 | ## Workflow / 工作流程 1. **Search** 使用 `search.mjs` 搜索报告（无需 API Key），返回报告内容，进行分析 2. **Get API Key** 前往 https://pc.yanbaoke.cn/openclaw 获取 API Key（下载必需） 3. **Download** 使用 `download.mjs`、UUID 和 API Key 下载报告 ## Error Codes / 错误码 | Code | Description | 说明 | |------|-------------|------| | 400 | Format not available | 请求的格式不可用，请检查搜索结果中的 `formats` 字段 | | 401 | Invalid or missing API Key | API Key 无效或缺失，请从 https://pc.yanbaoke.cn/openclaw 获取 | | 402 | Insufficient balance | 豆子不足，请前往充值: https://pc.yanbaoke.cn/pay 或者研报客app里面充值(每份报告消耗10 个豆) | | 404 | Report not found | 报告不存在，请检查 UUID 是否正确 | | 429 | Rate limit exceeded | 请求过于频繁，请稍后再试 | | 500 | Server error | 服务器错误，请稍后重试或联系客服 | ## Notes / 注意事项 - 搜索结果按发布时间倒序排列 / Results are sorted by publication date in descending order - 最大返回500条结果 / Maximum 500 results returned - 多个筛选条件使用逗号分隔，如：`--org "中信证券,华泰证券,国泰君安"` / Multiple filter values separated by commas - 下载链接60秒内有效，请及时下载 / Download link expires in 60 seconds - **搜索无需 API Key，下载需要 API Key** 获取地址[https://pc.yanbaoke.cn/openclaw](https://pc.yanbaoke.cn/openclaw) - 每份报告仅收费一次，已购买报告可重复下载 - 下载需要消耗豆（每份报告 10 个豆） - **格式说明**：默认下载 PDF 格式，部分报告支持 DOC 或 PPT 格式。搜索结果中的 `formats` 字段显示该报告支持的格式列表 ## API Reference / API 参考 ### Search API / 搜索 API ``` GET https://api.yanbaoke.cn/skills/search_report X-Skill-Version: 2.0.4 X-Skill-ID: yanbaoke-research-report-download ``` ### Download API / 下载 API ``` GET https://api.yanbaoke.cn/skills/report_download/{uuid}?format={format} [REDACTED_SECRET] X-Skill-Version: 2.0.4 X-Skill-ID: yanbaoke-research-report-download ``` **Query Parameters / 查询参数:** - `format` (optional): Document format - `pdf` (default), `doc`, or `ppt` / 文档格式（默认 pdf） **OpenAI-compatible format** - 使用与 OpenAI 相同的 Bearer Token 认证方式。 **Version Headers** - 所有API请求均包含版本号信息： - `X-Skill-Version`: 当前技能版本号（如 `2.0.4`） - `X-Skill-ID`: 技能唯一标识符（`yanbaoke-research-report-download`）

path: _meta.json
content: {
  "ownerId": "yanbaoke-research-report-download",
  "slug": "yanbaoke-research-report-download",
  "version": "2.1.0",
  "publishedAt": 1767545389426,
  "requires": {
    "bins": ["node"],
    "env": ["YANBAOKE_API_KEY"]
  }
}


path: instruct.md
content: # Install yanbaoke Skill

Follow these steps to install the yanbaoke skill for yourself. After installation, you will be able to search and download research reports from pc.yanbaoke.cn — giving you access to millions of industry reports from 5000+ institutions.

## Prerequisites

You need Node.js 18+ (already present if OpenClaw is installed).

**Search is free** - No API Key required for searching reports.

**Download requires API Key** - If you want to download reports, you need a valid `YANBAOKE_API_KEY`. Obtain one from https://pc.yanbaoke.cn/openclaw

## Steps

### 1. Create directories

```bash
mkdir -p ~/.openclaw/skills/yanbaoke/scripts
```

### 2. Download skill definition

```bash
curl -fSL https://app.quzili.cn/skills/yanbaoke/SKILL.md -o ~/.openclaw/skills/yanbaoke/SKILL.md
```

### 3. Download tool scripts

```bash
curl -fSL https://app.quzili.cn/skills/yanbaoke/scripts/search.mjs -o ~/.openclaw/skills/yanbaoke/scripts/search.mjs
curl -fSL https://app.quzili.cn/skills/yanbaoke/scripts/download.mjs -o ~/.openclaw/skills/yanbaoke/scripts/download.mjs
```

### 4. Set YANBAOKE_API_KEY (Optional - for downloads only)

If you want to download reports, write the key to `~/.bashrc` (or `~/.zshrc` if the user's shell is zsh) so it persists across sessions. Replace `<YOUR_API_KEY>` with the actual key from https://pc.yanbaoke.cn/openclaw

```bash
echo 'export YANBAOKE_API_KEY="<YOUR_API_KEY>"' >> ~/.bashrc
source ~/.bashrc
```

**Skip this step if you only need search functionality.**

### 5. Verify

Run all checks below. Every line must pass.

```bash
test -f ~/.openclaw/skills/yanbaoke/SKILL.md && echo "SKILL.md OK" || echo "SKILL.md MISSING"
test -f ~/.openclaw/skills/yanbaoke/scripts/search.mjs && echo "search.mjs OK" || echo "search.mjs MISSING"
test -f ~/.openclaw/skills/yanbaoke/scripts/download.mjs && echo "download.mjs OK" || echo "download.mjs MISSING"
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "人工智能" -n 3
```

The search command should return a list of research reports. If all checks pass, the installation is complete — you now have the yanbaoke skill and can search/download reports as described in SKILL.md.


path: README.md
content: # 研报客 Research Report Download Skill  (yanbaoke-research-report-download)


![banner](./banner.png)

从研报客平台（pc.yanbaoke.cn）搜索和下载研究报告的 OpenClaw Skill。平台覆盖 5000+ 家机构、五百万+ 份行业深度研究报告。

## 功能特性

- **搜索研报** - 无需 API Key，免费搜索百万级研究报告，获取报告 UUID 用于下载
- **下载报告** - 支持 PDF/DOC/PPT 格式下载（需要 API Key）
- **多维度筛选** - 支持按机构、报告类型、股票、日期范围、页数等条件筛选
- **投资建议** - AI 驱动的投资分析建议
- **双语支持** - 中文界面和文档，便于国内用户使用

## 安装

### 前置要求

- Node.js 18+
- OpenClaw

### 安装步骤

将此提示复制发送给你的openclaw，来安装yanbaoke 技能

请根据 https://app.quzili.cn/skills/yanbaoke/instruct.md 安装 yanbaoke 技能。


### 配置 API Key（可选）

如需下载报告，请前往 [https://pc.yanbaoke.cn/openclaw](https://pc.yanbaoke.cn/openclaw) 获取 API Key：

```bash
echo 'export YANBAOKE_API_KEY="sk-your-api-key"' >> ~/.bashrc
source ~/.bashrc
```

## 使用方法

### 搜索报告

```bash
# 基础搜索
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "人工智能"

# 指定数量
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "新能源汽车" -n 20

# 全文搜索
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "深度分析" --type content

# 机构筛选
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "半导体" --org "中信证券,华泰证券"

# 日期范围筛选
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "光伏" --start-date "2024-01-01" --end-date "2024-12-31"

# 页数筛选
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "深度分析" --min-pages 30 --max-pages 100

# 股票筛选
node ~/.openclaw/skills/yanbaoke/scripts/search.mjs "比亚迪" --stock "比亚迪"
```

### 下载报告

```bash
# 使用环境变量中的 API Key（默认 PDF 格式）
node ~/.openclaw/skills/yanbaoke/scripts/download.mjs <uuid>

# 直接传入 API Key
node ~/.openclaw/skills/yanbaoke/scripts/download.mjs <uuid> "sk-your-api-key"

# 指定下载格式
node ~/.openclaw/skills/yanbaoke/scripts/download.mjs <uuid> "sk-your-api-key" --format=doc
node ~/.openclaw/skills/yanbaoke/scripts/download.mjs <uuid> "sk-your-api-key" -f ppt
```

## 项目结构

```
yanbaoke-skill/
├── SKILL.md              # Skill 定义和文档
├── README.md             # 项目说明
├── instruct.md           # 安装指南
├── _meta.json            # Skill 元数据
├── banner.png            # 项目横幅
├── .gitignore            # Git 忽略规则
└── scripts/
    ├── version.mjs       # 版本配置
    ├── search.mjs        # 搜索脚本
    ├── download.mjs      # 下载脚本
```

## API 参考

### 搜索 API

```
GET https://api.yanbaoke.cn/skills/search_report
X-Skill-Version: 2.0.6
X-Skill-ID: yanbaoke-research-report-download
```

### 下载 API

```
GET https://api.yanbaoke.cn/skills/report_download/{uuid}?format={format}
[REDACTED_SECRET]
X-Skill-Version: 2.0.6
X-Skill-ID: yanbaoke-research-report-download
```

**格式参数**: `pdf`（默认）、`doc`、`ppt`

## 定价

| 用户类型 | 首次下载 | 再次下载 |
|---------|---------|---------|
| 用户 | 10 个豆 | 免费 |

每份报告仅收费一次，已购买报告可重复下载。

## 相关链接

- [研报客平台](https://pc.yanbaoke.cn)
- [API Key 获取](https://pc.yanbaoke.cn/openclaw)
- [详细文档](SKILL.md)

## 许可证

MIT License


path: scripts/version.mjs
content: /**
 * Version Configuration
 *
 * 版本号统一管理，与 _meta.json 保持一致
 * Version management - keep in sync with _meta.json
 */

export const VERSION = "2.1.0";

// API 版本检查接口
export const VERSION_CHECK_URL = "https://api.yanbaoke.cn/skills/version";

// Skill 标识符
export const SKILL_ID = "yanbaoke-research-report-download";


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

import { VERSION, SKILL_ID } from "./version.mjs";

/**
 * Research Report Download Script
 *
 * **Get API Key** from [https://pc.yanbaoke.cn/openclaw](https://pc.yanbaoke.cn/openclaw) (required for download) / 前往 https://pc.yanbaoke.cn/openclaw 获取 API Key（下载必需）
 * Usage:
 *   node download.mjs <uuid> [api_key] [options]
 *
 * Environment variables:
 *   YANBAOKE_API_KEY - Default API key (can be overridden by argument)
 *
 * Examples:
 *   node download.mjs "abc123-def456"
 *   node download.mjs "abc123-def456" "sk-your-api-key"
 */

function usage() {
  console.error(`Usage: download.mjs <uuid> [api_key] [options]

Arguments:
  uuid              Report UUID (required)
  api_key           API key for authentication (optional, uses YANBAOKE_API_KEY env var)

Options:
  -h, --help        Show this help message
  -f, --format      Document format: pdf (default), doc, or ppt

Environment Variables:
  YANBAOKE_API_KEY      Default API key for authentication

Examples:
  node {baseDir}/scripts/download.mjs "abc123-def456"
  node {baseDir}/scripts/download.mjs "abc123-def456" "sk-your-api-key"
  node {baseDir}/scripts/download.mjs "abc123-def456" "sk-your-api-key" --format=doc
  node {baseDir}/scripts/download.mjs "abc123-def456" "" -f ppt

Note: You can get your API key from https://pc.yanbaoke.cn/openclaw`);
  process.exit(2);
}

const args = process.argv.slice(2);

if (args.length === 0 || args.includes("-h") || args.includes("--help")) usage();

const uuid = args[0];
let apiKey = "";
let format = "pdf";  // 默认格式为 pdf

// Parse arguments
for (let i = 1; i < args.length; i++) {
  const arg = args[i];
  if (!arg.startsWith("-")) {
    apiKey = arg;
  } else if (arg === "-f" || arg === "--format") {
    // 下一个参数是格式值
    format = args[i + 1] || "pdf";
    i++;  // 跳过下一个参数
  } else if (arg.startsWith("--format=")) {
    // --format=value 格式
    format = arg.split("=")[1] || "pdf";
  } else if (arg.startsWith("-f=")) {
    // -f=value 格式
    format = arg.split("=")[1] || "pdf";
  }
}

// 验证格式参数
const validFormats = ["pdf", "doc", "ppt"];
if (!validFormats.includes(format.toLowerCase())) {
  console.error(`Error: Invalid format "${format}". Must be one of: pdf, doc, ppt`);
  process.exit(1);
}
format = format.toLowerCase();

[REDACTED_SECRET] || process.env.YANBAOKE_API_KEY || "";

if (!uuid) {
  console.error("Error: UUID is required");
  usage();
}

if (!apiKey) {
  console.error("Error: API key is required. Provide it as an argument or set YANBAOKE_API_KEY environment variable.");
  console.error("\nGet your API key from: https://pc.yanbaoke.cn/openclaw");
  process.exit(1);
}

try {
  // 构建下载 URL，包含格式参数
  const downloadUrl = `https://api.yanbaoke.cn/skills/report_download/${uuid}?format=${format}`;

  const resp = await fetch(downloadUrl, {
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "X-Skill-Version": VERSION,
      "X-Skill-ID": SKILL_ID,
    }
  });

  const body = await resp.json().catch(() => null);

  if (!resp.ok) {
    const errorMsg = body?.error?.message ?? body?.message ?? `HTTP ${resp.status}`;
    console.error(`❌ Download failed: ${errorMsg}`);

    // 友好的错误提示
    const suggestions = {
      400: `\n💡 该报告暂时没有 ${format.toUpperCase()} 格式。`,
      401: `\n🔑 获取 API Key: https://pc.yanbaoke.cn/openclaw`,
      402: `\n💰 豆子不足，请前往充值: https://pc.yanbaoke.cn/pay 或者研报客app里面充值(每份报告消耗10 个豆)`,
      404: `\n🔍 报告不存在，请检查 UUID 是否正确。您可以先运行搜索命令获取正确的 UUID。`,
      429: `\n⏳ 请求过于频繁，请稍后再试。`,
    };

    const suggestion = suggestions[resp.status];
    if (suggestion) {
      console.error(suggestion);
    }

    process.exit(1);
  }

  let report;
  // 检查是否是 OpenAI 格式响应
  if (body?.choices && body.choices[0]?.report) {
    report = body.choices[0].report;
  } else if (body?.download_url) {
    report = body;
  } else {
    console.error("Unexpected response format");
    console.error(JSON.stringify(body, null, 2));
    process.exit(1);
  }

  const { download_url, title, filename, format: reportFormat = format } = report;

  // 显示下载链接
  console.log(`## Report Download Link\n`);
  console.log(`**Title**: ${title}`);
  console.log(`**Format**: ${reportFormat.toUpperCase()}`);
  console.log(`**Filename**: ${filename}`);
  console.log(`**Expires in**: ${report.expires_in ?? 60} seconds\n`);
  console.log(`**Download URL** (click to download):`);
  console.log(`${download_url}\n`);
  console.log(`---\n`);
  console.log(`*Note: This download link expires in 60 seconds.*`);

} catch (err) {
  console.error(`Error: ${err.message}`);
  process.exit(1);
}


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

import { VERSION, SKILL_ID } from "./version.mjs";

function usage() {
  console.error(`Usage: search.mjs "keyword" [options]

Options:
  -n <count>         Number of results (default: 100, max: 500)
  --type <type>      Search type: title or content (default: title)
  --org <org>        Publisher/Institution (comma-separated for multiple)
  --report-type <type> Report type (comma-separated for multiple)
  --stock <stock>    Stock name (comma-separated for multiple)
  --start-date <date> Start date in YYYY-MM-DD format
  --end-date <date>   End date in YYYY-MM-DD format
  --min-pages <num>  Minimum page count
  --max-pages <num>  Maximum page count
  -h, --help         Show this help message`);
  process.exit(2);
}

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

const keyword = args[0];
let size = 100;
let searchType = "title";
let org = null;
let reportType = null;
let stock = null;
let startDate = null;
let endDate = null;
let minPages = null;
let maxPages = null;

// Date validation helper (YYYY-MM-DD format) - timezone-safe
function isValidDate(dateStr) {
  if (!dateStr) return false;
  const regex = /^(\d{4})-(\d{2})-(\d{2})$/;
  if (!regex.test(dateStr)) return false;
  const [_, year, month, day] = dateStr.match(regex);
  const date = new Date(Number(year), Number(month) - 1, Number(day));
  return date.getFullYear() === Number(year) &&
         date.getMonth() === Number(month) - 1 &&
         date.getDate() === Number(day);
}

// Number validation helper
function parseNumber(v, optionName) {
  const n = Number.parseInt(v, 10);
  if (isNaN(n) || v !== String(n)) {
    console.error(`Invalid value for ${optionName}: ${v}. Must be a number`);
    process.exit(1);
  }
  return n;
}

// Option handlers mapping
const optionHandlers = {
  "-n": (v) => size = parseNumber(v, "-n"),
  "--type": (v) => {
    searchType = v;
    if (searchType !== "title" && searchType !== "content") {
      console.error(`Invalid search_type: ${searchType}. Must be 'title' or 'content'`);
      process.exit(1);
    }
  },
  "--org": (v) => org = v,
  "--report-type": (v) => reportType = v,
  "--stock": (v) => stock = v,
  "--start-date": (v) => {
    if (!isValidDate(v)) {
      console.error(`Invalid start-date: ${v}. Must be in YYYY-MM-DD format`);
      process.exit(1);
    }
    startDate = v;
  },
  "--end-date": (v) => {
    if (!isValidDate(v)) {
      console.error(`Invalid end-date: ${v}. Must be in YYYY-MM-DD format`);
      process.exit(1);
    }
    endDate = v;
  },
  "--min-pages": (v) => minPages = parseNumber(v, "--min-pages"),
  "--max-pages": (v) => maxPages = parseNumber(v, "--max-pages"),
};

for (let i = 1; i < args.length; i++) {
  const a = args[i];
  const handler = optionHandlers[a];
  if (handler) {
    const value = args[++i];
    if (value === undefined) {
      console.error(`Option ${a} requires a value`);
      usage();
    }
    handler(value);
  } else {
    console.error(`Unknown arg: ${a}`);
    usage();
  }
}

const params = new URLSearchParams({
  keyword: keyword,
  size: String(Math.max(1, Math.min(size, 500))),
  search_type: searchType,
});

if (org) params.append("institution", org);
if (reportType) params.append("reporttype", reportType);
if (stock) params.append("stockname", stock);
if (startDate) params.append("startdate", startDate);
if (endDate) params.append("enddate", endDate);
if (minPages !== null) params.append("minpagenum", String(minPages));
if (maxPages !== null) params.append("maxpagenum", String(maxPages));

const resp = await fetch(`https://api.yanbaoke.cn/skills/search_report?${params}`, {
  headers: {
    "X-Skill-Version": VERSION,
    "X-Skill-ID": SKILL_ID,
  }
});

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

const result = await resp.json();

if (!result.success) {
  console.error(`API Error: ${result.message}`);
  process.exit(1);
}

// Print summary
console.log(`## ${result.message}\n`);
console.log(`Total: ${result.total} reports\n`);
console.log("---\n");

// Print results
const data = result.data ?? [];
console.log("## Reports\n");

for (const report of data) {
  const uuid = report?.uuid ?? "";
  const title = report?.title ?? "";
  const url = report?.url ?? "";
  const time = report?.time ?? "";
  const pagenum = report?.pagenum ?? 0;
  const orgName = report?.org_name ?? "";
  const rtypeName = report?.rtype_name ?? "";
  const content = report?.content ?? "";

  console.log(`- **${title}**`);
  if (orgName) console.log(`  Publisher: ${orgName}`);
  if (rtypeName) console.log(`  Type: ${rtypeName}`);
  if (pagenum) console.log(`  Pages: ${pagenum}`);
  if (time) console.log(`  Date: ${time}`);
  if (content) console.log(`  Content: ${content}`);
  if (uuid) console.log(`  UUID: ${uuid}`);
  console.log(`  ${url}`);
  console.log();
}


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