{"id":"092b5ac9-fad2-4207-a860-4b25196bfe7e","entityType":"agent","slug":"clawhub-huagavin-bidding-hunter","name":"定制化招标信息采集","canonicalUrl":"https://www.xpersona.co/agent/clawhub-huagavin-bidding-hunter","canonicalPath":"/agent/clawhub-huagavin-bidding-hunter","generatedAt":"2026-10-10T05:40:14.303Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-09T17:57:25.890Z","emptyReason":null},"description":"Automated government procurement bid discovery engine. Scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and delivers deadline-aware reports. Users configure their own keywords and monitoring targets; built-in adapters cover 5 major platforms with an extensible plugin model for adding more. Skill: 定制化招标信息采集 Owner: huagavin Summary: Automated government procurement bid discovery engine. Scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and delivers deadline-aware reports. Users configure their own keywords and monitoring targets; built-in adapters cover 5 major platforms with an extensible plugin model for adding more. Tags: latest:0.1.0 Version history: v0","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 2.2K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s179vrt2zv80p4y7g1hnkzrj1d8779gn:bidding-hunter","sourceUrl":"https://clawhub.ai/huagavin/bidding-hunter","homepage":"https://clawhub.ai/huagavin/skills/bidding-hunter","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/huagavin/bidding-hunter","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/huagavin/skills/bidding-hunter","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":67,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Automated government procurement bid discovery engine. Scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and del"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T17:57:25.890Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T17:57:25.890Z","emptyReason":null},"stars":null,"forks":null,"downloads":2168,"packageName":null,"latestVersion":"0.1.0","tractionLabel":"2.2K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T17:57:25.859Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T17:57:25.890Z","lastCrawledAt":"2026-10-09T17:57:25.859Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T17:57:25.859Z","lastVerifiedAt":null,"highlights":[{"version":"0.1.0","createdAt":"2026-08-09T13:57:46.145Z","changelog":"Initial release of bidding-hunter — an automated government procurement bid discovery engine. - Scans Chinese procurement platforms for bids matching user-configured keywords. - Tracks bid statuses, deadlines, and sends reminders through deadline-aware reports. - Users can add or customize target platforms using built-in adapters and an extensible plugin model. - All operations performed via the `bidding-hunter` CLI and standard tools; strict tool-discipline enforced. - Platform-agnostic; no ad-hoc scripts or browser automation outside defined adapters. - Detailed documentation and workflow for adding new platforms and tracking opportunities.","fileCount":47,"zipByteSize":96514}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s179vrt2zv80p4y7g1hnkzrj1d8779gn:bidding-hunter","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T05:40:14.301Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-huagavin-bidding-hunter/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-09T17:57:25.890Z","emptyReason":null},"readme":"Skill: 定制化招标信息采集\n\nOwner: huagavin\n\nSummary: Automated government procurement bid discovery engine. Scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and delivers deadline-aware reports. Users configure their own keywords and monitoring targets; built-in adapters cover 5 major platforms with an extensible plugin model for adding more.\n\nTags: latest:0.1.0\n\nVersion history:\n\nv0.1.0 | 2026-08-09T13:57:46.145Z | auto\n\nInitial release of bidding-hunter — an automated government procurement bid discovery engine.\n\n- Scans Chinese procurement platforms for bids matching user-configured keywords.\n- Tracks bid statuses, deadlines, and sends reminders through deadline-aware reports.\n- Users can add or customize target platforms using built-in adapters and an extensible plugin model.\n- All operations performed via the `bidding-hunter` CLI and standard tools; strict tool-discipline enforced.\n- Platform-agnostic; no ad-hoc scripts or browser automation outside defined adapters.\n- Detailed documentation and workflow for adding new platforms and tracking opportunities.\n\nArchive index:\n\nArchive v0.1.0: 47 files, 96514 bytes\n\nFiles: .github (0b), .github/workflows (0b), .github/workflows/test.yml (437b), .gitignore (363b), .npmignore (199b), bin (0b), bin/bidding-hunter.js (13928b), CHANGELOG.md (3498b), config (0b), config/default.yaml (6109b), config/schema.json (4510b), CONTRIBUTING.md (3180b), DESIGN.md (19773b), LICENSE (1084b), package-lock.json (66205b), package.json (1230b), README.md (10075b), scripts (0b), scripts/explore-platform.js (5343b), skill-card.md (2383b), SKILL.md (9531b), src (0b), src/config.js (5047b), src/constants.js (1830b), src/database.js (19744b), src/detail-fetcher.js (12182b), src/index.js (5511b), src/lock.js (2176b), src/matcher.js (2910b), src/notifier.js (7476b), src/platforms (0b), src/platforms/base.js (4661b), src/platforms/beijing.js (2645b), src/platforms/dalian.js (2633b), src/platforms/hebei.js (4320b), src/platforms/liaoning.js (3355b), src/platforms/national.js (7295b), src/platforms/registry.js (3798b), src/reminder.js (3246b), src/reporter.js (8068b), src/scanner.js (7802b), templates (0b), templates/platform-adapter.js (6204b), tests (0b), tests/database.test.js (5680b), tests/matcher.test.js (6089b), _meta.json (133b)\n\nFile v0.1.0:SKILL.md\n\n---\nname: bidding-hunter\ndescription: >-\n  Automated government procurement bid discovery engine. Scans multiple Chinese\n  procurement platforms, matches configurable keywords, tracks bid statuses,\n  and delivers deadline-aware reports. Users configure their own keywords and\n  monitoring targets; built-in adapters cover 5 major platforms with an\n  extensible plugin model for adding more.\nversion: \"1.0.1\"\ntags:\n  - bidding\n  - procurement\n  - government\n  - china\n  - automation\n  - scanner\n  - playwright\n  - bid-discovery\n---\n\n# Bidding Hunter — 投标发掘引擎\n\nA config-driven, reusable bid discovery engine that automates the tedious process of monitoring Chinese government procurement platforms.\n\n**Compatible with any AI assistant that can execute CLI tools:** OpenClaw, Claude Code, Codex CLI, Cursor Agent, and similar platforms. The skill is platform-agnostic — all operations are exposed through the `bidding-hunter` CLI and standard shell commands.\n\n## 🚨 MANDATORY TOOL DISCIPLINE — READ FIRST\n\n**ANY bidding-hunter task MUST use the existing tools below. You are FORBIDDEN from writing ad-hoc scripts, inline Node.js, curl, web_fetch, or any other approach.**\n\n### Existing Tools (the ONLY allowed tools for bidding-hunter work)\n\n| Tool | Purpose | Command |\n|------|---------|---------|\n| `bidding-hunter` CLI | All operations: scan, report, list, status, stats, export, remind, init, create-adapter, test-platform, config-validate | `bidding-hunter <command>` |\n| `scripts/explore-platform.js` | Explore a new platform's structure before writing an adapter | `node scripts/explore-platform.js --url <url> [--recon]` |\n| `templates/platform-adapter.js` | Template for creating new platform adapters | Used by `bidding-hunter create-adapter` |\n| Platform adapters | `src/platforms/*.js` — scan() methods executed by the scanner | Called internally by `bidding-hunter scan` |\n| Core modules | scanner, matcher, database, reporter, detail-fetcher, reminder, notifier | Called internally by the CLI |\n\n### Rules (ZERO EXCEPTIONS)\n\n1. **Use tools, don't replace them.** A bidding-hunter task is a tool-execution task, NOT a coding task.\n2. **No ad-hoc scripts.** Do NOT write `scrape_*.js`, `fetch_*.js`, `test_*.js`, inline `node -e \"...\"`, `curl`, `web_fetch`, or any one-off scraping code.\n3. **No browser shortcuts.** Do NOT use Playwright/Selenium directly unless you are inside a platform adapter's `scan()` method. All browser orchestration is handled by the scanner.\n4. **No manual dedup/matching.** Do NOT copy-paste from the matcher or database modules. Use `bidding-hunter scan` → `bidding-hunter report`.\n\n### Escalation Path (when something breaks)\n\n```\nExisting tool fails\n  → Read the tool's source code\n  → Fix the bug in the tool itself (edit src/*.js, bin/*.js, scripts/*.js, templates/*.js)\n  → Run tests: node --test tests/*.test.js\n  → Re-run the tool\n  → If the tool is fundamentally broken beyond repair: refactor the tool, record in memory/YYYY-MM-DD.md\n```\n\n**Key principle:** Fix the tool, don't work around it. Every workaround creates technical debt that compounds across sessions.\n\n**Memory requirement:** After ANY code change to the bidding-hunter project (including bug fixes, adapter changes, config schema updates), write a note to `memory/YYYY-MM-DD.md` recording what was changed and why.\n\n---\n\n## When to Use This Skill\n\n- You need to **discover relevant bidding opportunities** across multiple Chinese procurement platforms\n- You want **automated keyword matching** against daily procurement announcements\n- You need **deadline tracking** and **reminders** for approaching bid deadlines\n- You want to **add a new procurement platform** and let the agent figure out the scraping\n\n## Quick Start\n\n```bash\n# Create initial config\nbidding-hunter init\n\n# Edit config with YOUR keywords\n# vim ~/.bidding-hunter/config.yaml\n\n# Run a scan\nbidding-hunter scan\n\n# View results\nbidding-hunter report\n```\n\n## Core Workflow\n\n### 1. Configure Your Keywords\n\nEdit `~/.bidding-hunter/config.yaml` — this is where YOU define what to search for:\n\n```yaml\nmatching:\n  search_queries:        # What to type into platform search boxes\n    - \"建筑工程\"\n    - \"信息化\"\n    \n  tiers:                 # Multi-tier keyword matching\n    high:\n      label: \"L1\"\n      keywords: [\"建筑工程\", \"土建施工\", \"信息系统\"]\n    medium:\n      label: \"L2\"  \n      keywords: [\"系统集成\", \"运维服务\"]\n    low:\n      label: \"L3\"\n      keywords: [\"市政工程\", \"园林绿化\"]\n      \n  blacklist:             # Filter these out\n    - \"中标\"\n    - \"成交\"\n    - \"废标\"\n    - \"更正\"\n```\n\n**Important**: The agent should NOT decide keywords — the user provides them via config.\n\n### 2. Choose Your Platforms\n\n```yaml\nplatforms:\n  enabled:\n    - beijing    # 北京公共资源交易平台\n    - hebei      # 河北省招标投标公共服务平台\n    - liaoning   # 辽宁省公共资源交易平台\n    - dalian     # 大连市公共资源交易平台\n    - national   # 全国公共资源交易平台 (6 data sources)\n```\n\n### 3. Run the Scan\n\n```bash\nbidding-hunter scan\n```\n\nThis runs a deterministic pipeline: scan → match → ingest → detail fetch → reminders → report.\n\n### 4. Review & Manage Matches\n\n```bash\n# List tracked entries\nbidding-hunter list --status tracked\n\n# Mark as tracked\nbidding-hunter status --id 5 --status tracked\n\n# Update bid progress  \nbidding-hunter status --id 5 --bid-status submitted\n\n# Check reminders\nbidding-hunter remind\n```\n\n## Agent Usage Pattern\n\nWhen a user asks you to run or manage bidding discovery:\n\n1. **First time setup**: Run `bidding-hunter init`, then guide the user to configure keywords\n2. **Daily scan**: Run `bidding-hunter scan` — this is deterministic, no LLM needed\n3. **Review results**: Run `bidding-hunter report` and present findings to the user\n4. **Status updates**: When user says \"track #5\" or \"discard #3\", use `bidding-hunter status`\n5. **Adding platforms**: When user wants to monitor a new site:\n   ```bash\n   # Let the agent explore\n   node scripts/explore-platform.js --url https://new-site.ggzy.gov.cn/ --recon\n   # Then create adapter from findings\n   bidding-hunter create-adapter --name new-site\n   # Implement extraction logic based on exploration results\n   # Test it\n   bidding-hunter test-platform --name new-site\n   ```\n\n## Key Design Principles (For Agent)\n\n- **Deterministic core**: Scanning, matching, and ingestion do not depend on LLM availability\n- **Config over code**: All keywords, sites, and schedules are in user-editable YAML\n- **One adapter per platform**: Adding a new site = writing one ~50-line adapter file\n- **Idempotent**: Safe to re-run; URL-based dedup and checkpoint system\n- **Graceful degradation**: One failing site doesn't block others\n- **No privacy leakage**: All data stays local; no telemetry or cloud uploads\n\n## Built-in Platform Adapters\n\n| ID | Platform | Strategy |\n|----|----------|----------|\n| `beijing` | 北京公共资源交易平台 | URL pagination, <a> extraction |\n| `hebei` | 河北省招标投标公共服务平台 | fullsearch.html URL search + filter |\n| `liaoning` | 辽宁省公共资源交易平台 | API-based extraction + session cookie |\n| `dalian` | 大连市公共资源交易平台 | URL pagination |\n| `national` | 全国公共资源交易平台 | 6 data sources via UI interaction |\n\n## Adding a New Platform\n\n1. **Explore** the platform structure:\n   ```bash\n   node scripts/explore-platform.js --url https://new-site.gov.cn/ --recon\n   ```\n   This navigates to the site and captures structural hints.\n\n2. **Create adapter template**:\n   ```bash\n   bidding-hunter create-adapter --name new-site\n   ```\n\n3. **Implement** the `scan()` method with:\n   - Navigation logic (URL-based or search-based)\n   - `extractItems()` function with correct CSS selectors\n   - Pagination handling\n   - Error handling for site-specific edge cases\n\n4. **Test**:\n   ```bash\n   bidding-hunter test-platform --name new-site\n   ```\n\n5. **Enable** in config:\n   ```yaml\n   platforms:\n     enabled:\n       - new-site\n   ```\n\n## Operations\n\n### Checking Status\n\n```bash\n# View database stats\nbidding-hunter stats\n\n# Export for analysis\nbidding-hunter export --format csv > bids.csv\n```\n\n### Scheduling\n\nAdd to crontab:\n```bash\n0 16 * * * bidding-hunter scan --config ~/.bidding-hunter/config.yaml\n```\n\nOr use OpenClaw cron with the `bidding-hunter scan` command.\n\n### Notifications\n\nConfigure webhook channels in config:\n```yaml\nnotifications:\n  channels:\n    - type: feishu\n      webhook: \"${FEISHU_WEBHOOK_URL}\"\n    - type: slack\n      webhook: \"${SLACK_WEBHOOK_URL}\"\n```\n\n## Troubleshooting\n\n| Problem | Solution |\n|---------|----------|\n| Platform returns no data | Site may have changed — run `test-platform` to debug selectors |\n| Browser launch fails | Ensure Chromium is installed: `npx playwright install chromium` |\n| Database locked | Another scan is running — wait or remove `~/.bidding-hunter/.lock` |\n| Network timeouts | Increase `scan.retry_stairs` timeouts in config |\n\n## Files\n\n- **Config**: `~/.bidding-hunter/config.yaml`\n- **Database**: `~/.bidding-hunter/data.db` (SQLite)\n- **Scan results**: `~/.bidding-hunter/scan_results/YYYY-MM-DD/`\n- **Custom adapters**: `~/.bidding-hunter/platforms/*.js`\n\n## See Also\n\n- [DESIGN.md](./DESIGN.md) — Full architecture and extension guide\n- [README.md](./README.md) — User-facing documentation\n- [CONTRIBUTING.md](./CONTRIBUTING.md) — How to contribute adapters\n\nFile v0.1.0:README.md\n\n# 投标猎手 🎯\n\n> 每天自动帮你盯着各大政府采购平台，发现匹配的招标机会就通知你。  \n> 不用一个个网站去翻，不用怕错过截止日期——它帮你干这些重复活儿。\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)\n\n---\n\n## 这是什么？\n\n如果你或者你的公司需要经常找政府采购项目去投标，你一定经历过这些事：\n\n- 每天打开好几个政府采购网站，一个一个搜关键词\n- 搜出来的结果里混杂着中标公告、更正公告、废标公告，得人工过滤\n- 记下项目名称、截止日期、预算金额，生怕漏了哪一个\n- 不同省份的网站格式还不一样，换个省份又要重新适应\n\n**投标猎手就是帮你干这些事的。** 它自动访问你关心的政府采购平台，按照你设置的关键词筛选招标公告，过滤掉无关信息，把结果整理好汇报给你。\n\n### 它具体能做什么\n\n1. **自动扫描** —— 定时访问北京、河北、辽宁、大连、全国等政府采购平台，不用你手动打开浏览器\n2. **关键词匹配** —— 你告诉它关心什么业务（比如\"建筑工程\"\"信息系统\"），它按重要程度分三级筛选\n3. **过滤噪音** —— 自动跳过中标公告、废标公告、更正公告等跟你没关系的内容\n4. **提取关键信息** —— 自动从公告详情页提取投标截止时间、开标时间、预算金额\n5. **跟踪进度** —— 记录每个项目的状态（关注中 → 已投标 → 中标/未中标），全程可追溯\n6. **到期提醒** —— 快截止的项目主动提醒你，不会因为忙忘了投\n7. **通知推送** —— 扫描结果可以发到飞书、钉钉、企微，或者导出文件\n\n---\n\n## 谁适合用？\n\n- 📋 **每天需要找投标机会的商务、销售、市场人员** —— 不用再手动翻网页\n- 🏢 **投标团队的办公室文员** —— 一个人盯几个省份的标，效率翻倍\n- 🤖 **有 AI 助手的人**（OpenClaw、Claude Code 等）—— 可以让 AI 帮你管理和分析投标数据\n\n---\n\n## 怎么装？\n\n### 第一步：装 Node.js\n\n投标猎手需要 Node.js 运行环境。如果你的电脑上已经有 Node.js（版本 ≥ 18），跳过这一步。\n\n**没装过的话**，去 [nodejs.org](https://nodejs.org) 下载安装包，选 LTS 版本（左边的按钮），像装普通软件一样双击安装就行。\n\n装好后打开终端（Windows 上是 PowerShell 或 CMD，Mac 上是终端），输入下面命令确认装好了：\n\n```bash\nnode --version\n# 应该显示类似 v18.x.x 或 v20.x.x\n```\n\n### 第二步：装投标猎手\n\n在终端里输入：\n\n```bash\nnpm install -g bidding-hunter\n```\n\n> 💡 `npm install -g` 的意思是\"全局安装\"，装一次就行，之后在任何目录都能用。\n\n### 第三步：装浏览器驱动\n\n投标猎手需要一个自动化浏览器来访问网页，运行下面命令：\n\n```bash\nnpx playwright install chromium\n```\n\n> 💡 这个命令会下载一个 Chromium 浏览器（Chrome 的开源版本），大约 150MB。只需要运行一次。\n\n### 第四步：创建配置文件\n\n```bash\nbidding-hunter init\n```\n\n这个命令会在你的电脑上创建 `~/.bidding-hunter/config.yaml` 配置文件。接下来你需要编辑它，告诉投标猎手你关心什么关键词。\n\n---\n\n## 配置你的关键词\n\n配置文件是最重要的——你告诉投标猎手你要找什么，它就帮你找什么。\n\n用任意文本编辑器打开 `~/.bidding-hunter/config.yaml`（Windows 上路径是 `C:\\Users\\你的用户名\\.bidding-hunter\\config.yaml`）：\n\n```yaml\nmatching:\n  # 搜索词：在每个平台搜索框里输入什么\n  # 一个搜索词就是一次搜索，建议 2-4 个，太多反而慢\n  search_queries:\n    - \"建筑工程\"\n    - \"信息化\"\n\n  # 关键词分级：匹配标题时按优先级筛选\n  tiers:\n    high:                          # 第一优先级：核心业务\n      label: \"重点项目\"\n      keywords: [\"建筑工程\", \"土建施工\", \"信息系统\"]\n    medium:                        # 第二优先级：相关业务\n      label: \"一般项目\"\n      keywords: [\"系统集成\", \"运维服务\", \"弱电工程\"]\n    low:                           # 第三优先级：拓展业务\n      label: \"关注项目\"\n      keywords: [\"市政工程\", \"园林绿化\", \"物业管理\"]\n\n  # 黑名单：标题里包含这些词的就跳过\n  # 这些都是\"已经结束/不相关\"的信号\n  blacklist:\n    - \"中标\"\n    - \"成交\"\n    - \"废标\"\n    - \"更正\"\n    - \"流标\"\n    - \"终止\"\n    - \"澄清\"\n\n# 要扫描哪些平台\nplatforms:\n  enabled:\n    - beijing        # 北京公共资源交易平台\n    - hebei          # 河北省招标投标公共服务平台\n    - liaoning       # 辽宁省公共资源交易平台\n    - dalian         # 大连市公共资源交易平台\n    - national       # 全国公共资源交易平台（覆盖最广）\n```\n\n> ⚠️ **关键词怎么选？** 建议用你的核心业务词汇，2-4 个字一组，不要太长。比如你是做弱电工程的，用\"弱电\"\"安防\"\"智能化\"就比\"弱电系统工程设计与施工\"效果好——搜索词越短，命中率越高。\n\n---\n\n## 日常使用\n\n### 每天扫一次\n\n```bash\nbidding-hunter scan\n```\n\n运行后它会逐个打开你配置的平台，搜索关键词，筛选匹配的公告。扫描完会自动保存结果。\n\n整个过程大概 2-5 分钟（取决于开了几个平台），你可以在终端看到实时进度。\n\n### 看今天的报告\n\n```bash\nbidding-hunter report --today\n```\n\n输出类似这样：\n\n```\n📊 投标猎手日报 — 2026-07-23\n\n────────────────────────────────────────────\n⏱️ 扫描概览\n  北京平台    扫描 45 条，新匹配 3 条\n  河北平台    扫描 32 条，新匹配 2 条\n  全国平台    扫描 120 条，新匹配 5 条\n────────────────────────────────────────────\n\n🔴 重点项目 (8)\n  1. [北京] XX市政务云平台建设项目 — 预算 500万\n     📅 投标截止: 2026-08-15  |  🔗 https://...\n  2. [全国] XX省智慧交通系统采购 — 预算 1200万\n     📅 开标时间: 2026-08-20  |  🔗 https://...\n  ...\n\n🟡 一般项目 (5)\n  3. [河北] XX学校网络改造工程\n     📅 投标截止: 2026-08-10  |  🔗 https://...\n  ...\n\n🟢 关注项目 (3)\n  ...\n```\n\n### 查看和更新项目状态\n\n```bash\n# 列出所有跟踪中的项目\nbidding-hunter list\n\n# 把某个项目标记为\"已投标\"\nbidding-hunter status --id 5 --status submitted\n\n# 查看状态统计\nbidding-hunter stats\n```\n\n状态可以设为：`watching`（关注中）、`submitted`（已投标）、`won`（中标）、`lost`（未中标）\n\n### 查看即将到期的项目\n\n```bash\nbidding-hunter remind\n```\n\n会列出投标截止日期在 3 天内的项目，帮你防止漏投。\n\n### 导出数据\n\n```bash\nbidding-hunter export --format csv --output ~/Desktop/投标记录.csv\n```\n\n---\n\n## 每天自动运行（可选）\n\n如果你希望每天下午 4 点自动扫描（不需要手动敲命令），可以让技术人员帮你设置定时任务。\n\n或者如果你用的是 OpenClaw，可以直接让它帮你设置：\n\n> \"帮我在投标猎手里加一个每天下午 4 点的定时扫描任务\"\n\n---\n\n## 内置平台\n\n| 平台 ID | 名称 | 说明 |\n|---------|------|------|\n| `beijing` | 北京公共资源交易平台 | 北京地区采购公告 |\n| `hebei` | 河北省招标投标公共服务平台 | 河北省招标信息 |\n| `liaoning` | 辽宁省公共资源交易平台 | 辽宁省采购公告 |\n| `dalian` | 大连市公共资源交易平台 | 大连市采购公告 |\n| `national` | 全国公共资源交易平台 | 覆盖最广，包含 6 个数据源（省平台、央企、财政部、自然资源部、国资委、商务部） |\n\n---\n\n## 需要其他省份的平台？\n\n投标猎手支持扩展新的政府采购平台。如果你需要其他省份或城市的平台，可以联系技术人员添加，或者让 AI 助手帮你探索：\n\n```bash\nbidding-hunter explore --url \"https://你要添加的平台网址/\"\n```\n\n---\n\n## 隐私说明\n\n- 📁 所有数据都存在你自己的电脑上（`~/.bidding-hunter/` 目录）\n- 🔒 不上传任何数据到云端，不收集任何使用统计\n- 🔑 你的关键词和投标数据只有你自己能看到\n\n---\n\n## 常见问题\n\n**Q: 扫描太慢怎么办？**\nA: 可以在配置文件里减少 `platforms.enabled` 中的平台数量，或者减少 `search_queries` 的搜索词数量。\n\n**Q: 搜出来太多不相关的结果？**\nA: 检查你的关键词是否太宽泛。试试用更精确的词，或者增加黑名单关键词。\n\n**Q: 某些平台扫不出结果？**\nA: 政府采购网站可能会改版。如果某个平台突然失效，可以联系技术人员更新适配器。\n\n**Q: 需要一直开着电脑吗？**\nA: 不需要。只有运行 `bidding-hunter scan` 的时候才需要。扫完就可以关掉。\n\n---\n\n## 技术架构（供开发人员参考）\n\n```\n配置文件 (YAML) → 扫描器 → 关键词匹配 → 数据库 (SQLite) → 报告生成 → 通知推送\n                      │                    │\n                      │                    ├── 详情抓取（日期/预算）\n                      │                    └── 到期提醒\n                      │\n                      └── 平台适配器（每个采购网站一个）\n```\n\n详细设计见 [DESIGN.md](DESIGN.md)，参与贡献见 [CONTRIBUTING.md](CONTRIBUTING.md)。\n\n---\n\n## 许可证\n\nMIT © 2026\n\n---\n\n**免责声明**：本工具自动访问公开的政府采购网站。请合理使用，尊重网站的访问频率限制。使用者需自行承担使用风险，作者不对任何滥用或违反网站服务条款的行为负责。\n\nFile v0.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn727b0hk04906tj8vp8kzrb3x8763rk\",\n  \"slug\": \"bidding-hunter\",\n  \"version\": \"0.1.0\",\n  \"publishedAt\": 1786283866145\n}\n\nFile v0.1.0:CHANGELOG.md\n\n# Changelog\n\nAll notable changes to Bidding Hunter will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [1.0.1] - 2026-07-22\n\n### Fixed\n- Critical: Added missing `database.ingest()` function to both SQLite and JSON backends\n- Critical: YAML `wait_until` silently ignored by `gotoWithRetry` (snake_case ↔ camelCase mismatch) — added normalization in config.js\n- Critical: `/home/$USER` literal string bug in 3 files (database.js, lock.js, notifier.js) — replaced with `path.join('/home', process.env.USER)`\n- Warning: `updates.note` vs `updates.notes` inconsistency in SQLite history recording\n- Warning: `package.json` repository URL was placeholder (`user/bidding-hunter`)\n- Warning: Example keywords in README.md exposed business domain — replaced with generic construction procurement terms\n- Warning: Platform-specific URL transformation in detail-fetcher.js extracted to adapter `transformDetailUrl()` hook\n- Warning: national.js DATA_SOURCES reordered 1→6 to sequential 1→2→3→4→5→6\n- Warning: hebei.js and liaoning.js had hardcoded default query `['视频']` — removed, errors instead\n- Info: SKILL.md example keywords synced with README (generic construction/IT terms)\n- Info: `fetchDetails` builds URL transform closure once (was O(n×m) adapter loading)\n- Info: CLI `fetchDetail` falls back to registry for URL transform when no options passed\n- Info: database.js `new Database()` wrapped in try/catch for graceful fallback when native binding missing\n- Info: `notifier.js` uses centralized `resolvePath()` instead of inline regex\n- Info: `DATE_RE` global regex state protected with try-finally\n- Reporter: Skip empty scan stats line when no stats available\n\n### Changed\n- SKILL.md: Added mandatory tool-discipline section (agents must use existing tools, not write ad-hoc scripts)\n- SKILL.md: Added platform compatibility note (works with any AI assistant that can execute CLI tools)\n- BasePlatformAdapter: Added `transformDetailUrl(url)` hook for platform-specific URL transformations\n- Registry: Added `findTransformDetailUrl()` for adapter URL transform lookup\n- Scanner: Null element tracking and filtering during item collection\n- Scanner: `politeDelay` from config passed to adapterContext (was hardcoded per-adapter)\n- All platform adapters: use `ctx.politeDelay` instead of hardcoded `waitForTimeout` values\n\n## [1.0.0] - 2026-07-22\n\n### Added\n- Core scanning engine with pluggable platform adapter system\n- 5 built-in platform adapters: Beijing, Hebei, Liaoning, Dalian, National\n- Configurable multi-tier keyword matching engine\n- SQLite database with JSON fallback for storage\n- Detail page fetcher with automatic date/budget/method extraction\n- Deadline tracking and reminder engine (urgent/open-results/missing-dates)\n- Multi-channel notification system (stdout, file, webhook, Feishu, DingTalk, Slack)\n- Markdown and JSON report generation\n- CLI with 12 commands (scan, report, list, status, remind, stats, export, init, explore, create-adapter, test-platform)\n- File-based lock for concurrent-run prevention\n- Checkpoint system for idempotent runs\n- URL-based deduplication across platforms\n- Platform exploration helper for adding new sites\n- Platform adapter template with documentation\n- Comprehensive test suite with fixtures\n- Full documentation: README, DESIGN.md, CONTRIBUTING.md, SKILL.md\n\nFile v0.1.0:CONTRIBUTING.md\n\n# Contributing to Bidding Hunter\n\nThanks for your interest in contributing! This project automates government procurement bid discovery — every new platform adapter helps more people find relevant opportunities.\n\n## Ways to Contribute\n\n### 🔌 Add a New Platform Adapter\n\nThis is the most valuable contribution! Adding support for a new regional procurement platform.\n\n1. **Check existing platforms**: See `src/platforms/` for examples\n2. **Use the exploration tool**: `node scripts/explore-platform.js --url <url> --recon`\n3. **Create from template**: `bidding-hunter create-adapter --name <id>`\n4. **Implement the adapter**:\n   - `meta` — Platform metadata\n   - `scan(context, config)` — Main scanning logic\n   - `extractItems(page, { today, fromDate })` — DOM extraction\n5. **Test**: `bidding-hunter test-platform --name <id>`\n6. **Submit PR** with:\n   - The adapter file in `src/platforms/`\n   - Updated README platform list\n   - Brief description of the platform\n\n### 🐛 Bug Reports\n\n- Check existing issues first\n- Include: platform name, error message, steps to reproduce\n- If possible, include browser console output or page structure changes\n\n### 💡 Feature Requests\n\nOpen an issue with:\n- What you want to achieve\n- Why the current system doesn't support it\n- How you envision it working\n\n### 🧪 Testing\n\n```bash\n# Run unit tests\nnpm test\n\n# Test a specific platform\nbidding-hunter test-platform --name <id>\n\n# Run a dry-run scan\nbidding-hunter scan --dry-run\n```\n\n## Development Setup\n\n```bash\ngit clone https://github.com/user/bidding-hunter.git\ncd bidding-hunter\nnpm install\nnpx playwright install chromium\n\n# Create test config\nbidding-hunter init\n```\n\n## Platform Adapter Guidelines\n\nA good platform adapter:\n\n1. **Is defensive**: Handles network timeouts, changed selectors, no-data states\n2. **Uses retry stairs**: 30s → 45s → 60s with `gotoWithRetry`\n3. **Respects rate limits**: Uses delays between actions\n4. **Extracts clean data**: Returns `{ site, region, title, date, url }`\n5. **Is self-contained**: One file, no external dependencies beyond Playwright\n6. **Has clear logging**: Console messages in `site [query]` format\n7. **Handles pagination**: Button-based or URL-based with max page limit\n8. **Filters by date window**: Only returns items within `fromDate ~ today`\n\n### Adapter Checklist\n\n- [ ] `meta.id` is lowercase, no spaces, unique\n- [ ] `meta.url` points to the listing page (not detail page)\n- [ ] `scan()` creates its own browser context + page\n- [ ] Uses `this.gotoWithRetry()` for navigation\n- [ ] `extractItems()` uses `page.$$eval()` with serialized options\n- [ ] Date extraction handles multiple formats (YYYY-MM-DD, YYYY/MM/DD, etc.)\n- [ ] Returns items with all required fields\n- [ ] Closes browser context in `finally` block\n- [ ] Tested with `bidding-hunter test-platform`\n\n## Code Style\n\n- ES6+ with `'use strict'`\n- 2-space indentation\n- Single quotes for strings (except template literals)\n- `async/await` for all async operations\n- Descriptive variable names\n- Chinese comments for platform-specific logic\n\n## License\n\nBy contributing, you agree that your contributions will be licensed under the MIT License.\n\nFile v0.1.0:DESIGN.md\n\n# Bidding Hunter — Architecture & Design\n\n> A reusable, config-driven government procurement bid discovery engine.\n> Generalizes proven patterns from production use across 5 Chinese procurement platforms.\n\n## Table of Contents\n\n1. [Design Philosophy](#1-design-philosophy)\n2. [System Architecture](#2-system-architecture)\n3. [Data Model](#3-data-model)\n4. [Platform Adapter System](#4-platform-adapter-system)\n5. [Matching Engine](#5-matching-engine)\n6. [Pipeline & Idempotency](#6-pipeline--idempotency)\n7. [Notification System](#7-notification-system)\n8. [Config Schema](#8-config-schema)\n9. [Extension Guide](#9-extension-guide)\n\n---\n\n## 1. Design Philosophy\n\n### Principles\n\n| Principle | Rationale |\n|-----------|-----------|\n| **Deterministic core, optional LLM** | Scanning/matching/ingestion must not depend on AI model availability |\n| **Config over code** | Keywords, sites, schedules, notifications all live in user-editable config |\n| **Platform adapters are plugins** | Adding a new procurement site = writing one adapter file |\n| **Idempotent by design** | Lock files, checkpoints, URL-based dedup — safe to re-run |\n| **Graceful degradation** | One failing site doesn't block others; one failing adapter doesn't crash the pipeline |\n| **Agent-compatible output** | All outputs available as structured JSON for LLM consumption |\n\n### What This Is (and Isn't)\n\n- ✅ A CLI tool for automated bid discovery across multiple platforms\n- ✅ A framework for adding new procurement platforms\n- ✅ A notification system for bid deadlines and status tracking\n- ❌ A web scraper for arbitrary websites\n- ❌ A bidding/bid-submission tool\n- ❌ A guarantee of finding every possible bid\n\n---\n\n## 2. System Architecture\n\n```\n┌───────────────────────────────────────────────────────────────┐\n│                        CLI / Cron Trigger                       │\n│                    bidding-hunter scan --config=...             │\n└───────────────────────────┬───────────────────────────────────┘\n                            │\n        ┌───────────────────▼────────────────────┐\n        │              Scanner (orchestrator)      │\n        │  ┌───────────────────────────────────┐  │\n        │  │       Platform Adapter Registry    │  │\n        │  │  ┌──────┐ ┌──────┐ ┌──────────┐  │  │\n        │  │  │Beijing│ │Hebei │ │ National │  │  │\n        │  │  └──┬───┘ └──┬───┘ └────┬─────┘  │  │\n        │  │     │         │          │        │  │\n        │  │  ┌──▼───┐ ┌──▼───┐ ┌────▼─────┐  │  │\n        │  │  │Liao..│ │Dalian│ │ User-Add │  │  │\n        │  │  └──────┘ └──────┘ └──────────┘  │  │\n        │  └───────────────────────────────────┘  │\n        └───────────────────┬────────────────────┘\n                            │ Raw results (JSON)\n        ┌───────────────────▼────────────────────┐\n        │              Matcher Engine              │\n        │   Keyword config → title matching        │\n        │   Tier system (L1/L2/L3 customizable)    │\n        │   Blacklist filtering                    │\n        └───────────────────┬────────────────────┘\n                            │ Matched entries\n        ┌───────────────────▼────────────────────┐\n        │              Database (SQLite)           │\n        │   URL-based dedup, status tracking       │\n        │   History, deadlines, result tracking    │\n        └───────────────────┬────────────────────┘\n                            │\n        ┌───────────────────▼────────────────────┐\n        │           Detail Fetcher (optional)      │\n        │   Extract deadlines, budget, method      │\n        └───────────────────┬────────────────────┘\n                            │\n        ┌───────────────────▼────────────────────┐\n        │           Reporter + Reminder            │\n        │   Markdown/text report                   │\n        │   Deadline alerts                        │\n        │   Status summaries                       │\n        └───────────────────┬────────────────────┘\n                            │\n        ┌───────────────────▼────────────────────┐\n        │              Notifier                    │\n        │   Webhook → Feishu / DingTalk / Slack   │\n        │   File output                            │\n        │   stdout (for cron capture)              │\n        └─────────────────────────────────────────┘\n```\n\n### Data Flow\n\n```\nConfig (YAML)\n    │\n    ├──► Scanner reads platform list, keywords, date window\n    │       │\n    │       ├──► For each platform: adapter.scan() → raw items[]\n    │       │\n    │       └──► Merge, deduplicate by URL → scan_results/\n    │\n    ├──► Matcher reads keyword tiers, blacklist\n    │       │\n    │       └──► For each raw item: match title → level + keyword\n    │\n    ├──► Database ingests new matches (URL dedup)\n    │       │\n    │       ├──► Assign sequential alias\n    │       ├──► Set status=undecided (default)\n    │       └──► Track first_seen, history\n    │\n    ├──► Detail Fetcher (for tracked entries)\n    │       │\n    │       └──► Open URL → extract dates, budget, method\n    │\n    ├──► Reminder scans tracked entries for deadlines\n    │       │\n    │       └──► urgent / open_results / missing_dates\n    │\n    └──► Reporter + Notifier\n            │\n            └──► Generate report → dispatch via configured channels\n```\n\n---\n\n## 3. Data Model\n\n### Database Schema (SQLite)\n\n```sql\n-- Bid entries: the core table\nCREATE TABLE IF NOT EXISTS entries (\n    id          INTEGER PRIMARY KEY AUTOINCREMENT,\n    alias       INTEGER NOT NULL UNIQUE,       -- sequential #1, #2...\n    title       TEXT NOT NULL,\n    url         TEXT NOT NULL UNIQUE,           -- natural key for dedup\n    site        TEXT NOT NULL,                  -- platform name\n    region      TEXT DEFAULT '',\n    pub_date    TEXT NOT NULL,                  -- YYYY-MM-DD\n    match_level TEXT DEFAULT '',                -- L1/L2/L3 or custom\n    match_kw    TEXT DEFAULT '',                -- which keyword matched\n    status      TEXT DEFAULT 'undecided',       -- tracked/undecided/discarded\n    bid_status  TEXT DEFAULT NULL,              -- bid progress state\n    first_seen  TEXT NOT NULL,\n    last_updated TEXT NOT NULL,\n    raw_json    TEXT DEFAULT '{}'               -- full raw data from scanner\n);\n\n-- Deadlines extracted from detail pages\nCREATE TABLE IF NOT EXISTS deadlines (\n    entry_id    INTEGER REFERENCES entries(id),\n    type        TEXT NOT NULL,                  -- bid_submit, bid_open, etc.\n    date        TEXT NOT NULL,                  -- YYYY-MM-DD\n    source      TEXT DEFAULT 'auto'             -- auto | manual\n);\n\n-- Status change history (audit trail)\nCREATE TABLE IF NOT EXISTS history (\n    entry_id    INTEGER REFERENCES entries(id),\n    date        TEXT NOT NULL,\n    event       TEXT NOT NULL,\n    detail      TEXT DEFAULT ''\n);\n\n-- Scan execution log\nCREATE TABLE IF NOT EXISTS scan_log (\n    id          INTEGER PRIMARY KEY AUTOINCREMENT,\n    date        TEXT NOT NULL,\n    site        TEXT NOT NULL,\n    scanned     INTEGER DEFAULT 0,\n    new_matches INTEGER DEFAULT 0,\n    status      TEXT DEFAULT 'ok',              -- ok | partial | failed\n    error       TEXT DEFAULT NULL,\n    started_at  TEXT NOT NULL,\n    ended_at    TEXT NOT NULL\n);\n\n-- Reported URLs (for dedup across runs)\nCREATE TABLE IF NOT EXISTS reported_urls (\n    url         TEXT PRIMARY KEY,\n    first_seen  TEXT NOT NULL\n);\n\n-- Configurable status labels (localized)\nCREATE TABLE IF NOT EXISTS status_labels (\n    key         TEXT PRIMARY KEY,               -- undecided, tracked, discarded...\n    label       TEXT NOT NULL,                  -- 待定, 关注, 放弃...\n    color       TEXT DEFAULT ''\n);\n```\n\n### Entry Lifecycle State Machine\n\n```\n                    ┌──────────┐\n                    │ undecided│ (default, new match)\n                    └────┬─────┘\n                         │\n              ┌──────────┼──────────┐\n              ▼          │          ▼\n        ┌──────────┐    │    ┌───────────┐\n        │ tracked  │    │    │ discarded │ (terminal)\n        └────┬─────┘    │    └───────────┘\n             │          │\n    ┌────────┼──────────┼─────────────┐\n    ▼        ▼          ▼             ▼\n┌──────┐ ┌──────┐  ┌────────┐  ┌──────────┐\n│watching│ │bought │  │prepared│  │submitted │\n└──┬───┘ │docs   │  │docs    │  └────┬─────┘\n   │     └──┬───┘  └───┬────┘       │\n   │        │          │            ▼\n   │        │          │      ┌──────────┐\n   │        │          │      │ opened   │\n   │        │          │      └────┬─────┘\n   │        │          │           │\n   │        │          │   ┌───────┼───────┐\n   │        │          │   ▼       │       ▼\n   │        │          │ ┌──────┐  │  ┌──────────┐\n   │        │          │ │ won  │  │  │ lost     │\n   │        │          │ └──────┘  │  └──────────┘\n   │        │          │           │\n   └────────┴──────────┴───────────┘\n            (all paths converge at won/lost)\n```\n\n---\n\n## 4. Platform Adapter System\n\n### Adapter Interface\n\nEvery platform adapter must export:\n\n```javascript\nmodule.exports = {\n  meta: {\n    id: 'beijing',           // unique identifier\n    name: 'Beijing GGZY',    // human-readable name\n    url: 'https://...',      // platform homepage\n    version: '1.0.0',\n  },\n  \n  // Required: scan the platform, return raw items\n  async scan(context, config) → {\n    items: [{ title, url, date, region, raw: {...} }],\n    stats: { scanned: N, errors: [] }\n  },\n  \n  // Optional: custom extraction for detail pages\n  async fetchDetail(context, url) → {\n    bid_submit, bid_open, budget, procurement_method, error\n  },\n  \n  // Optional: custom matching logic (falls back to default)\n  matchTitle?(title, keywords) → { level, keyword } | null,\n  \n  // Optional: custom dedup key (defaults to URL)\n  dedupKey?(item) → string,\n};\n```\n\n### Context Object\n\nThe `context` passed to adapters provides shared resources:\n\n```javascript\n{\n  browser: Playwright Browser,      // shared browser instance\n  logger: Logger,                   // scoped logger\n  cache: Cache,                     // optional cache store\n  reportedUrls: Set<string>,        // already-processed URLs\n  dateWindow: { from, to },         // scan date range\n  timeout: {                        // timeout config\n    page: [30000, 45000, 60000],   // retry stairs\n    waitUntil: ['domcontentloaded', 'domcontentloaded', 'networkidle']\n  }\n}\n```\n\n### Adapter Discovery\n\nAdapters are auto-discovered from:\n1. `src/platforms/*.js` (built-in)\n2. `~/.bidding-hunter/platforms/*.js` (user-defined)\n3. Config `platforms.customPath`\n\nAt load time, the registry validates each adapter against the interface.\n\n---\n\n## 5. Matching Engine\n\n### Config Structure\n\n```yaml\nmatching:\n  search_queries:              # What to type into search boxes\n    - \"视频\"\n    - \"宣传\"\n    - \"拍摄\"\n  \n  tiers:                       # Tiered keyword groups\n    high:\n      label: \"L1\"\n      keywords: [\"视频制作\", \"宣传视频\", \"宣传片\"]\n    medium:\n      label: \"L2\"\n      keywords: [\"会务执行\", \"会议服务\", \"活动策划\"]\n    low:\n      label: \"L3\"\n      keywords: [\"文化传媒\", \"拍摄制作\", \"后期制作\"]\n  \n  blacklist:                   # Titles containing these are filtered\n    - \"中标\"\n    - \"成交\"\n    - \"废标\"\n    - \"更正\"\n    - \"变更\"\n```\n\n### Matching Algorithm\n\n```\nfor each raw_item:\n  title = normalize(raw_item.title)\n  \n  // Blacklist check first\n  if any(blacklist_word in title):\n    skip\n  \n  // Tier match (exact substring, case-insensitive)\n  for tier in [high, medium, low]:\n    for keyword in tier.keywords:\n      if keyword in title:\n        match = { level: tier.label, keyword }\n        break\n    \n  if match:\n    matched_items.push({ ...raw_item, match })\n```\n\n### Custom Matching\n\nPlatform adapters can override matching for platform-specific logic:\n\n```javascript\n// In platform adapter:\nasync matchTitle(context, title) {\n  // Custom logic, e.g., check additional metadata\n  const customLevel = await context.page.evaluate(/* ... */);\n  return customLevel;\n}\n```\n\n---\n\n## 6. Pipeline & Idempotency\n\n### Run Lifecycle\n\n```\n1. Acquire lock (PID + timestamp)\n   ├── If stale (>2h): break stale lock, re-acquire\n   └── If active: exit with error\n   \n2. Load config + database\n   \n3. For each platform (parallel with concurrency limit):\n   ├── Skip if already completed today (checkpoint)\n   ├── Run adapter.scan()\n   ├── Save partial results to scan_results/YYYY-MM-DD/\n   └── Log to scan_log table\n   \n4. Merge + deduplicate across platforms\n   \n5. Match keywords → assign levels\n   \n6. Ingest into database:\n   ├── Skip existing URLs\n   ├── Assign new aliases\n   └── Create history entries\n   \n7. Fetch details (for tracked entries without deadlines)\n   ├── Parallel with concurrency limit\n   └── Respect per-platform rate limits\n   \n8. Build reminders\n   \n9. Generate + dispatch report\n   \n10. Update checkpoints, release lock\n```\n\n### Checkpoint System\n\n```\nscan_results/\n├── 2026-07-22/\n│   ├── beijing.json\n│   ├── hebei.json\n│   ├── _manifest.json        # { date, complete, failed_sites }\n│   └── _report.md\n```\n\nAll writes use atomic rename (write to `.tmp`, then `rename`).\n\n### Lock File\n\n```\n~/.bidding-hunter/.lock\n{ \"pid\": 12345, \"startedAt\": \"2026-07-22T16:00:00+08:00\" }\n```\n\nStale after 2 hours. Automatic cleanup in `finally` block.\n\n---\n\n## 7. Notification System\n\n### Channel Types\n\n| Channel | Protocol | Config |\n|---------|----------|--------|\n| `stdout` | Print to stdout | (none) |\n| `file` | Write to file | `path` |\n| `webhook` | HTTP POST JSON | `url`, `headers` |\n| `feishu` | Feishu bot webhook | Pre-configured template |\n| `dingtalk` | DingTalk bot webhook | Pre-configured template |\n| `slack` | Slack incoming webhook | Pre-configured template |\n\n### Notification Template\n\nTemplates use Handlebars-style `{{variable}}` interpolation:\n\n```yaml\nnotifications:\n  channels:\n    - type: feishu\n      webhook: \"https://open.feishu.cn/...\"\n      template: |\n        📋 {{date}} 招标情报\n        \n        🆕 新增匹配 {{new_count}} 条\n        {{#each new_matches}}\n        #{{alias}} [{{level}}] {{title}}\n           {{region}} · {{pub_date}} · {{status}}\n           🔗 {{url}}\n        {{/each}}\n        \n        ⏱️ 扫描统计：{{#each stats}}{{@key}}({{scanned}}) {{/each}}\n        \n        {{#if reminders}}\n        📌 待办提醒\n        {{#each reminders}}\n        🔴 {{title}} · 截止 {{deadline}}\n        {{/each}}\n        {{/if}}\n```\n\n---\n\n## 8. Config Schema\n\nFull config reference in `config/default.yaml`. Key sections:\n\n```yaml\n# === Required ===\nmatching:           # Keywords and matching rules\nplatforms:          # Which platforms to scan\n  enabled: [...]    # List of platform IDs\n\n# === Optional ===\ndatabase:\n  path: \"~/.bidding-hunter/data.db\"\n  type: \"sqlite\"    # sqlite (future: postgres)\n\nscan:\n  date_window: 2    # Days to look back\n  max_pages: 15     # Max pages per search\n  concurrency: 3    # Max concurrent platform scans\n  retry_stairs: [30000, 45000, 60000]\n  retry_wait: [domcontentloaded, domcontentloaded, networkidle]\n\nnotifications:\n  channels: []\n  \nschedule:\n  cron: \"0 16 * * *\"   # When to run (for cron integration)\n  \ndetail_fetch:\n  enabled: true\n  concurrency: 2\n  \nsessions:\n  max_tokens: 60000    # For persistent context compaction\n```\n\n---\n\n## 9. Extension Guide\n\n### Adding a New Platform\n\n1. **Run exploration mode**:\n   ```bash\n   bidding-hunter explore --url \"https://new-platform.gov.cn/...\"\n   ```\n   The agent will navigate the site, identify listing pages, pagination, and extraction selectors.\n\n2. **Create adapter from template**:\n   ```bash\n   bidding-hunter create-adapter --name \"my-platform\"\n   ```\n   This generates `~/.bidding-hunter/platforms/my-platform.js` from the template.\n\n3. **Implement the adapter** (3 required methods):\n   - `meta` — platform metadata\n   - `scan(context, config)` — extraction logic\n   - `dedupKey(item)` — uniqueness key (usually URL)\n\n4. **Test**:\n   ```bash\n   bidding-hunter test-platform --name \"my-platform\"\n   ```\n\n5. **Enable in config**:\n   ```yaml\n   platforms:\n     enabled:\n       - my-platform\n   ```\n\n### Custom Notification Channel\n\n```javascript\n// ~/.bidding-hunter/notifiers/my-channel.js\nmodule.exports = {\n  name: 'my-channel',\n  async send(report, config) {\n    // Custom dispatch logic\n  }\n};\n```\n\nThen in config:\n```yaml\nnotifications:\n  channels:\n    - type: custom\n      path: \"~/.bidding-hunter/notifiers/my-channel.js\"\n```\n\n---\n\n## Appendix: Security Considerations\n\n- All URLs validated before navigation (protocol whitelist: http/https only)\n- Browser runs in sandbox mode by default\n- No credentials stored in config (use environment variables or credential store)\n- Lock file prevents concurrent runs\n- Database uses parameterized queries (SQL injection safe)\n- Config validation via JSON Schema before execution\n\nFile v0.1.0:skill-card.md\n\n## Description:\n\nAutomated government procurement bid discovery engine that scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and delivers deadline-aware reports.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[huagavin](https://clawhub.ai/user/huagavin)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nBusiness, sales, procurement, and bid teams use this skill to monitor Chinese government procurement platforms for opportunities that match user-configured keywords, then review reports, deadlines, and bid status updates. Developers and agent operators can extend coverage by adding platform adapters.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill browses user-supplied procurement sites with weak containment.\n\nMitigation: Review before installing and run it in a contained environment or dedicated low-privilege account.\n\nRisk: Custom local JavaScript adapters can be loaded with broad privileges.\n\nMitigation: Avoid custom adapters unless their code is trusted and reviewed, and prefer deployments that constrain plugin loading.\n\nRisk: Webhook notifications can expose sensitive opportunity data to third-party services.\n\nMitigation: Enable webhooks only after accepting the data exposure and validating notification destinations.\n\n## Reference(s):\n\n- [Server-resolved source repository](https://github.com/HuaGavin/bidding-hunter)\n- [ClawHub skill page](https://clawhub.ai/huagavin/skills/bidding-hunter)\n- [README](README.md)\n- [Architecture and design](DESIGN.md)\n- [Configuration schema](config/schema.json)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown and CLI output with JSON, CSV, and configuration file artifacts]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Reports may include matched procurement notices, deadlines, bid statuses, reminders, and exportable local data.]\n\n## Skill Version(s):\n\n0.1.0 (source: ClawHub release metadata; artifact source reports 1.0.1)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v0.1.0:config/schema.json\n\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Bidding Hunter Configuration\",\n  \"type\": \"object\",\n  \"required\": [\"matching\"],\n  \"properties\": {\n    \"matching\": {\n      \"type\": \"object\",\n      \"required\": [\"tiers\", \"blacklist\"],\n      \"properties\": {\n        \"search_queries\": {\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\", \"minLength\": 1 },\n          \"minItems\": 1\n        },\n        \"tiers\": {\n          \"type\": \"object\",\n          \"minProperties\": 1,\n          \"additionalProperties\": {\n            \"type\": \"object\",\n            \"required\": [\"label\", \"keywords\"],\n            \"properties\": {\n              \"label\": { \"type\": \"string\" },\n              \"description\": { \"type\": \"string\" },\n              \"keywords\": {\n                \"type\": \"array\",\n                \"items\": { \"type\": \"string\", \"minLength\": 1 },\n                \"minItems\": 1\n              }\n            }\n          }\n        },\n        \"blacklist\": {\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" }\n        },\n        \"case_sensitive\": { \"type\": \"boolean\", \"default\": false }\n      }\n    },\n    \"database\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"path\": { \"type\": \"string\", \"default\": \"~/.bidding-hunter/data.db\" },\n        \"backup_before_scan\": { \"type\": \"boolean\", \"default\": true }\n      }\n    },\n    \"scan\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"date_window\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 30, \"default\": 2 },\n        \"max_pages\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 50, \"default\": 15 },\n        \"concurrency\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 10, \"default\": 3 },\n        \"retry_stairs\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"object\",\n            \"required\": [\"timeout\", \"wait_until\"],\n            \"properties\": {\n              \"timeout\": { \"type\": \"integer\", \"minimum\": 5000 },\n              \"wait_until\": { \"enum\": [\"load\", \"domcontentloaded\", \"networkidle\"] }\n            }\n          }\n        },\n        \"polite_delay\": { \"type\": \"integer\", \"minimum\": 0, \"default\": 2500 },\n        \"results_dir\": { \"type\": \"string\" },\n        \"user_agent\": { \"type\": \"string\" }\n      }\n    },\n    \"platforms\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" }\n        },\n        \"custom_paths\": {\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" }\n        },\n        \"overrides\": { \"type\": \"object\" }\n      }\n    },\n    \"detail_fetch\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"enabled\": { \"type\": \"boolean\", \"default\": true },\n        \"concurrency\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 5, \"default\": 2 },\n        \"skip_if_has_deadlines\": { \"type\": \"boolean\", \"default\": true },\n        \"skip_today_retry\": { \"type\": \"boolean\", \"default\": true },\n        \"max_age_days\": { \"type\": \"integer\", \"minimum\": 1, \"default\": 30 }\n      }\n    },\n    \"reminders\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"urgent_days\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 30, \"default\": 7 },\n        \"include_yesterday\": { \"type\": \"boolean\", \"default\": true }\n      }\n    },\n    \"notifications\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"channels\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"object\",\n            \"required\": [\"type\"],\n            \"properties\": {\n              \"type\": { \"type\": \"string\" }\n            }\n          }\n        }\n      }\n    },\n    \"report\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"max_title_length\": { \"type\": \"integer\", \"minimum\": 10, \"maximum\": 200, \"default\": 60 },\n        \"show_stats\": { \"type\": \"boolean\", \"default\": true },\n        \"show_reminders\": { \"type\": \"boolean\", \"default\": true },\n        \"show_undecided_prompt\": { \"type\": \"boolean\", \"default\": true }\n      }\n    },\n    \"schedule\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"cron\": { \"type\": \"string\" },\n        \"timezone\": { \"type\": \"string\" }\n      }\n    },\n    \"sessions\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"max_tokens\": { \"type\": \"integer\", \"minimum\": 1000, \"default\": 60000 }\n      }\n    },\n    \"logging\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"level\": { \"enum\": [\"debug\", \"info\", \"warn\", \"error\"], \"default\": \"info\" },\n        \"file\": { \"type\": \"string\" },\n        \"stderr\": { \"type\": \"boolean\", \"default\": true }\n      }\n    }\n  }\n}\n\nFile v0.1.0:package-lock.json\n\n{\n  \"name\": \"bidding-hunter\",\n  \"version\": \"1.0.1\",\n  \"lockfileVersion\": 3,\n  \"requires\": true,\n  \"packages\": {\n    \"\": {\n      \"name\": \"bidding-hunter\",\n      \"version\": \"1.0.1\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ajv\": \"^8.12.0\",\n        \"better-sqlite3\": \"^11.0.0\",\n        \"chalk\": \"^5.3.0\",\n        \"commander\": \"^12.0.0\",\n        \"dayjs\": \"^1.11.0\",\n        \"playwright\": \"^1.40.0\",\n        \"yaml\": \"^2.3.0\"\n      },\n      \"bin\": {\n        \"bidding-hunter\": \"bin/bidding-hunter.js\"\n      },\n      \"devDependencies\": {\n        \"eslint\": \"^8.50.0\"\n      },\n      \"engines\": {\n        \"node\": \">=18.0.0\"\n      }\n    },\n    \"node_modules/@eslint-community/eslint-utils\": {\n      \"version\": \"4.10.1\",\n      \"resolved\": \"https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz\",\n      \"integrity\": \"sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"eslint-visitor-keys\": \"^3.4.3\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      },\n      \"peerDependencies\": {\n        \"eslint\": \"^6.0.0 || ^7.0.0 || >=8.0.0\"\n      }\n    },\n    \"node_modules/@eslint-community/regexpp\": {\n      \"version\": \"4.12.2\",\n      \"resolved\": \"https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz\",\n      \"integrity\": \"sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \"^12.0.0 || ^14.0.0 || >=16.0.0\"\n      }\n    },\n    \"node_modules/@eslint/eslintrc\": {\n      \"version\": \"2.1.4\",\n      \"resolved\": \"https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz\",\n      \"integrity\": \"sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ajv\": \"^6.12.4\",\n        \"debug\": \"^4.3.2\",\n        \"espree\": \"^9.6.0\",\n        \"globals\": \"^13.19.0\",\n        \"ignore\": \"^5.2.0\",\n        \"import-fresh\": \"^3.2.1\",\n        \"js-yaml\": \"^4.1.0\",\n        \"minimatch\": \"^3.1.2\",\n        \"strip-json-comments\": \"^3.1.1\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/@eslint/eslintrc/node_modules/ajv\": {\n      \"version\": \"6.15.0\",\n      \"resolved\": \"https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz\",\n      \"integrity\": \"sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"fast-deep-equal\": \"^3.1.1\",\n        \"fast-json-stable-stringify\": \"^2.0.0\",\n        \"json-schema-traverse\": \"^0.4.1\",\n        \"uri-js\": \"^4.2.2\"\n      },\n      \"funding\": {\n        \"type\": \"github\",\n        \"url\": \"https://github.com/sponsors/epoberezkin\"\n      }\n    },\n    \"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse\": {\n      \"version\": \"0.4.1\",\n      \"resolved\": \"https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz\",\n      \"integrity\": \"sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@eslint/js\": {\n      \"version\": \"8.57.1\",\n      \"resolved\": \"https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz\",\n      \"integrity\": \"sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      }\n    },\n    \"node_modules/@humanwhocodes/config-array\": {\n      \"version\": \"0.13.0\",\n      \"resolved\": \"https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz\",\n      \"integrity\": \"sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==\",\n      \"deprecated\": \"Use @eslint/config-array instead\",\n      \"dev\": true,\n      \"license\": \"Apache-2.0\",\n      \"dependencies\": {\n        \"@humanwhocodes/object-schema\": \"^2.0.3\",\n        \"debug\": \"^4.3.1\",\n        \"minimatch\": \"^3.0.5\"\n      },\n      \"engines\": {\n        \"node\": \">=10.10.0\"\n      }\n    },\n    \"node_modules/@humanwhocodes/module-importer\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz\",\n      \"integrity\": \"sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==\",\n      \"dev\": true,\n      \"license\": \"Apache-2.0\",\n      \"engines\": {\n        \"node\": \">=12.22\"\n      },\n      \"funding\": {\n        \"type\": \"github\",\n        \"url\": \"https://github.com/sponsors/nzakas\"\n      }\n    },\n    \"node_modules/@humanwhocodes/object-schema\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz\",\n      \"integrity\": \"sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==\",\n      \"deprecated\": \"Use @eslint/object-schema instead\",\n      \"dev\": true,\n      \"license\": \"BSD-3-Clause\"\n    },\n    \"node_modules/@nodelib/fs.scandir\": {\n      \"version\": \"2.1.5\",\n      \"resolved\": \"https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz\",\n      \"integrity\": \"sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@nodelib/fs.stat\": \"2.0.5\",\n        \"run-parallel\": \"^1.1.9\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/@nodelib/fs.stat\": {\n      \"version\": \"2.0.5\",\n      \"resolved\": \"https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz\",\n      \"integrity\": \"sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/@nodelib/fs.walk\": {\n      \"version\": \"1.2.8\",\n      \"resolved\": \"https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz\",\n      \"integrity\": \"sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@nodelib/fs.scandir\": \"2.1.5\",\n        \"fastq\": \"^1.6.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/@ungap/structured-clone\": {\n      \"version\": \"1.3.3\",\n      \"resolved\": \"https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.3.tgz\",\n      \"integrity\": \"sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==\",\n      \"dev\": true,\n      \"license\": \"ISC\"\n    },\n    \"node_modules/acorn\": {\n      \"version\": \"8.17.0\",\n      \"resolved\": \"https://registry.npmmirror.com/acorn/-/acorn-8.17.0.tgz\",\n      \"integrity\": \"sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"bin\": {\n        \"acorn\": \"bin/acorn\"\n      },\n      \"engines\": {\n        \"node\": \">=0.4.0\"\n      }\n    },\n    \"node_modules/acorn-jsx\": {\n      \"version\": \"5.3.2\",\n      \"resolved\": \"https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz\",\n      \"integrity\": \"sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"peerDependencies\": {\n        \"acorn\": \"^6.0.0 || ^7.0.0 || ^8.0.0\"\n      }\n    },\n    \"node_modules/ajv\": {\n      \"version\": \"8.20.0\",\n      \"resolved\": \"https://registry.npmmirror.com/ajv/-/ajv-8.20.0.tgz\",\n      \"integrity\": \"sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"fast-deep-equal\": \"^3.1.3\",\n        \"fast-uri\": \"^3.0.1\",\n        \"json-schema-traverse\": \"^1.0.0\",\n        \"require-from-string\": \"^2.0.2\"\n      },\n      \"funding\": {\n        \"type\": \"github\",\n        \"url\": \"https://github.com/sponsors/epoberezkin\"\n      }\n    },\n    \"node_modules/ansi-regex\": {\n      \"version\": \"5.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz\",\n      \"integrity\": \"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/ansi-styles\": {\n      \"version\": \"4.3.0\",\n      \"resolved\": \"https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz\",\n      \"integrity\": \"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"color-convert\": \"^2.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/ansi-styles?sponsor=1\"\n      }\n    },\n    \"node_modules/argparse\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz\",\n      \"integrity\": \"sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==\",\n      \"dev\": true,\n      \"license\": \"Python-2.0\"\n    },\n    \"node_modules/balanced-match\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz\",\n      \"integrity\": \"sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/base64-js\": {\n      \"version\": \"1.5.1\",\n      \"resolved\": \"https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz\",\n      \"integrity\": \"sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==\",\n      \"funding\": [\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/feross\"\n        },\n        {\n          \"type\": \"patreon\",\n          \"url\": \"https://www.patreon.com/feross\"\n        },\n        {\n          \"type\": \"consulting\",\n          \"url\": \"https://feross.org/support\"\n        }\n      ],\n      \"license\": \"MIT\"\n    },\n    \"node_modules/better-sqlite3\": {\n      \"version\": \"11.10.0\",\n      \"resolved\": \"https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz\",\n      \"integrity\": \"sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==\",\n      \"hasInstallScript\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"bindings\": \"^1.5.0\",\n        \"prebuild-install\": \"^7.1.1\"\n      }\n    },\n    \"node_modules/bindings\": {\n      \"version\": \"1.5.0\",\n      \"resolved\": \"https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz\",\n      \"integrity\": \"sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"file-uri-to-path\": \"1.0.0\"\n      }\n    },\n    \"node_modules/bl\": {\n      \"version\": \"4.1.0\",\n      \"resolved\": \"https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz\",\n      \"integrity\": \"sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"buffer\": \"^5.5.0\",\n        \"inherits\": \"^2.0.4\",\n        \"readable-stream\": \"^3.4.0\"\n      }\n    },\n    \"node_modules/brace-expansion\": {\n      \"version\": \"1.1.16\",\n      \"resolved\": \"https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.16.tgz\",\n      \"integrity\": \"sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"balanced-match\": \"^1.0.0\",\n        \"concat-map\": \"0.0.1\"\n      }\n    },\n    \"node_modules/buffer\": {\n      \"version\": \"5.7.1\",\n      \"resolved\": \"https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz\",\n      \"integrity\": \"sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==\",\n      \"funding\": [\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/feross\"\n        },\n        {\n          \"type\": \"patreon\",\n          \"url\": \"https://www.patreon.com/feross\"\n        },\n        {\n          \"type\": \"consulting\",\n          \"url\": \"https://feross.org/support\"\n        }\n      ],\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"base64-js\": \"^1.3.1\",\n        \"ieee754\": \"^1.1.13\"\n      }\n    },\n    \"node_modules/callsites\": {\n      \"version\": \"3.1.0\",\n      \"resolved\": \"https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz\",\n      \"integrity\": \"sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6\"\n      }\n    },\n    \"node_modules/chalk\": {\n      \"version\": \"5.6.2\",\n      \"resolved\": \"https://registry.npmmirror.com/chalk/-/chalk-5.6.2.tgz\",\n      \"integrity\": \"sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \"^12.17.0 || ^14.13 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/chalk?sponsor=1\"\n      }\n    },\n    \"node_modules/chownr\": {\n      \"version\": \"1.1.4\",\n      \"resolved\": \"https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz\",\n      \"integrity\": \"sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==\",\n      \"license\": \"ISC\"\n    },\n    \"node_modules/color-convert\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz\",\n      \"integrity\": \"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"color-name\": \"~1.1.4\"\n      },\n      \"engines\": {\n        \"node\": \">=7.0.0\"\n      }\n    },\n    \"node_modules/color-name\": {\n      \"version\": \"1.1.4\",\n      \"resolved\": \"https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz\",\n      \"integrity\": \"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/commander\": {\n      \"version\": \"12.1.0\",\n      \"resolved\": \"https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz\",\n      \"integrity\": \"sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/concat-map\": {\n      \"version\": \"0.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz\",\n      \"integrity\": \"sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/cross-spawn\": {\n      \"version\": \"7.0.6\",\n      \"resolved\": \"https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz\",\n      \"integrity\": \"sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"path-key\": \"^3.1.0\",\n        \"shebang-command\": \"^2.0.0\",\n        \"which\": \"^2.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/dayjs\": {\n      \"version\": \"1.11.21\",\n      \"resolved\": \"https://registry.npmmirror.com/dayjs/-/dayjs-1.11.21.tgz\",\n      \"integrity\": \"sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/debug\": {\n      \"version\": \"4.4.3\",\n      \"resolved\": \"https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz\",\n      \"integrity\": \"sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ms\": \"^2.1.3\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0\"\n      },\n      \"peerDependenciesMeta\": {\n        \"supports-color\": {\n          \"optional\": true\n        }\n      }\n    },\n    \"node_modules/decompress-response\": {\n      \"version\": \"6.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz\",\n      \"integrity\": \"sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"mimic-response\": \"^3.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/deep-extend\": {\n      \"version\": \"0.6.0\",\n      \"resolved\": \"https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz\",\n      \"integrity\": \"sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=4.0.0\"\n      }\n    },\n    \"node_modules/deep-is\": {\n      \"version\": \"0.1.4\",\n      \"resolved\": \"https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz\",\n      \"integrity\": \"sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/detect-libc\": {\n      \"version\": \"2.1.2\",\n      \"resolved\": \"https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz\",\n      \"integrity\": \"sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==\",\n      \"license\": \"Apache-2.0\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/doctrine\": {\n      \"version\": \"3.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz\",\n      \"integrity\": \"sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==\",\n      \"dev\": true,\n      \"license\": \"Apache-2.0\",\n      \"dependencies\": {\n        \"esutils\": \"^2.0.2\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0.0\"\n      }\n    },\n    \"node_modules/end-of-stream\": {\n      \"version\": \"1.4.5\",\n      \"resolved\": \"https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz\",\n      \"integrity\": \"sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"once\": \"^1.4.0\"\n      }\n    },\n    \"node_modules/escape-string-regexp\": {\n      \"version\": \"4.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz\",\n      \"integrity\": \"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/eslint\": {\n      \"version\": \"8.57.1\",\n      \"resolved\": \"https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz\",\n      \"integrity\": \"sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==\",\n      \"deprecated\": \"This version is no longer supported. Please see https://eslint.org/version-support for other options.\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@eslint-community/eslint-utils\": \"^4.2.0\",\n        \"@eslint-community/regexpp\": \"^4.6.1\",\n        \"@eslint/eslintrc\": \"^2.1.4\",\n        \"@eslint/js\": \"8.57.1\",\n        \"@humanwhocodes/config-array\": \"^0.13.0\",\n        \"@humanwhocodes/module-importer\": \"^1.0.1\",\n        \"@nodelib/fs.walk\": \"^1.2.8\",\n        \"@ungap/structured-clone\": \"^1.2.0\",\n        \"ajv\": \"^6.12.4\",\n        \"chalk\": \"^4.0.0\",\n        \"cross-spawn\": \"^7.0.2\",\n        \"debug\": \"^4.3.2\",\n        \"doctrine\": \"^3.0.0\",\n        \"escape-string-regexp\": \"^4.0.0\",\n        \"eslint-scope\": \"^7.2.2\",\n        \"eslint-visitor-keys\": \"^3.4.3\",\n        \"espree\": \"^9.6.1\",\n        \"esquery\": \"^1.4.2\",\n        \"esutils\": \"^2.0.2\",\n        \"fast-deep-equal\": \"^3.1.3\",\n        \"file-entry-cache\": \"^6.0.1\",\n        \"find-up\": \"^5.0.0\",\n        \"glob-parent\": \"^6.0.2\",\n        \"globals\": \"^13.19.0\",\n        \"graphemer\": \"^1.4.0\",\n        \"ignore\": \"^5.2.0\",\n        \"imurmurhash\": \"^0.1.4\",\n        \"is-glob\": \"^4.0.0\",\n        \"is-path-inside\": \"^3.0.3\",\n        \"js-yaml\": \"^4.1.0\",\n        \"json-stable-stringify-without-jsonify\": \"^1.0.1\",\n        \"levn\": \"^0.4.1\",\n        \"lodash.merge\": \"^4.6.2\",\n        \"minimatch\": \"^3.1.2\",\n        \"natural-compare\": \"^1.4.0\",\n        \"optionator\": \"^0.9.3\",\n        \"strip-ansi\": \"^6.0.1\",\n        \"text-table\": \"^0.2.0\"\n      },\n      \"bin\": {\n        \"eslint\": \"bin/eslint.js\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/eslint-scope\": {\n      \"version\": \"7.2.2\",\n      \"resolved\": \"https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz\",\n      \"integrity\": \"sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==\",\n      \"dev\": true,\n      \"license\": \"BSD-2-Clause\",\n      \"dependencies\": {\n        \"esrecurse\": \"^4.3.0\",\n        \"estraverse\": \"^5.2.0\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/eslint-visitor-keys\": {\n      \"version\": \"3.4.3\",\n      \"resolved\": \"https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz\",\n      \"integrity\": \"sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==\",\n      \"dev\": true,\n      \"license\": \"Apache-2.0\",\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/eslint/node_modules/ajv\": {\n      \"version\": \"6.15.0\",\n      \"resolved\": \"https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz\",\n      \"integrity\": \"sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"fast-deep-equal\": \"^3.1.1\",\n        \"fast-json-stable-stringify\": \"^2.0.0\",\n        \"json-schema-traverse\": \"^0.4.1\",\n        \"uri-js\": \"^4.2.2\"\n      },\n      \"funding\": {\n        \"type\": \"github\",\n        \"url\": \"https://github.com/sponsors/epoberezkin\"\n      }\n    },\n    \"node_modules/eslint/node_modules/chalk\": {\n      \"version\": \"4.1.2\",\n      \"resolved\": \"https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz\",\n      \"integrity\": \"sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ansi-styles\": \"^4.1.0\",\n        \"supports-color\": \"^7.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/chalk?sponsor=1\"\n      }\n    },\n    \"node_modules/eslint/node_modules/json-schema-traverse\": {\n      \"version\": \"0.4.1\",\n      \"resolved\": \"https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz\",\n      \"integrity\": \"sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/espree\": {\n      \"version\": \"9.6.1\",\n      \"resolved\": \"https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz\",\n      \"integrity\": \"sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==\",\n      \"dev\": true,\n      \"license\": \"BSD-2-Clause\",\n      \"dependencies\": {\n        \"acorn\": \"^8.9.0\",\n        \"acorn-jsx\": \"^5.3.2\",\n        \"eslint-visitor-keys\": \"^3.4.1\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/esquery\": {\n      \"version\": \"1.7.0\",\n      \"resolved\": \"https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz\",\n      \"integrity\": \"sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==\",\n      \"dev\": true,\n      \"license\": \"BSD-3-Clause\",\n      \"dependencies\": {\n        \"estraverse\": \"^5.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=0.10\"\n      }\n    },\n    \"node_modules/esrecurse\": {\n      \"version\": \"4.3.0\",\n      \"resolved\": \"https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz\",\n      \"integrity\": \"sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==\",\n      \"dev\": true,\n      \"license\": \"BSD-2-Clause\",\n      \"dependencies\": {\n        \"estraverse\": \"^5.2.0\"\n      },\n      \"engines\": {\n        \"node\": \">=4.0\"\n      }\n    },\n    \"node_modules/estraverse\": {\n      \"version\": \"5.3.0\",\n      \"resolved\": \"https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz\",\n      \"integrity\": \"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==\",\n      \"dev\": true,\n      \"license\": \"BSD-2-Clause\",\n      \"engines\": {\n        \"node\": \">=4.0\"\n      }\n    },\n    \"node_modules/esutils\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz\",\n      \"integrity\": \"sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==\",\n      \"dev\": true,\n      \"license\": \"BSD-2-Clause\",\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/expand-template\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz\",\n      \"integrity\": \"sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==\",\n      \"license\": \"(MIT OR WTFPL)\",\n      \"engines\": {\n        \"node\": \">=6\"\n      }\n    },\n    \"node_modules/fast-deep-equal\": {\n      \"version\": \"3.1.3\",\n      \"resolved\": \"https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz\",\n      \"integrity\": \"sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/fast-json-stable-stringify\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz\",\n      \"integrity\": \"sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/fast-levenshtein\": {\n      \"version\": \"2.0.6\",\n      \"resolved\": \"https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz\",\n      \"integrity\": \"sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/fast-uri\": {\n      \"version\": \"3.1.4\",\n      \"resolved\": \"https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.4.tgz\",\n      \"integrity\": \"sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==\",\n      \"funding\": [\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/fastify\"\n        },\n        {\n          \"type\": \"opencollective\",\n          \"url\": \"https://opencollective.com/fastify\"\n        }\n      ],\n      \"license\": \"BSD-3-Clause\"\n    },\n    \"node_modules/fastq\": {\n      \"version\": \"1.20.1\",\n      \"resolved\": \"https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz\",\n      \"integrity\": \"sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"reusify\": \"^1.0.4\"\n      }\n    },\n    \"node_modules/file-entry-cache\": {\n      \"version\": \"6.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz\",\n      \"integrity\": \"sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"flat-cache\": \"^3.0.4\"\n      },\n      \"engines\": {\n        \"node\": \"^10.12.0 || >=12.0.0\"\n      }\n    },\n    \"node_modules/file-uri-to-path\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz\",\n      \"integrity\": \"sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/find-up\": {\n      \"version\": \"5.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz\",\n      \"integrity\": \"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"locate-path\": \"^6.0.0\",\n        \"path-exists\": \"^4.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/flat-cache\": {\n      \"version\": \"3.2.0\",\n      \"resolved\": \"https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz\",\n      \"integrity\": \"sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"flatted\": \"^3.2.9\",\n        \"keyv\": \"^4.5.3\",\n        \"rimraf\": \"^3.0.2\"\n      },\n      \"engines\": {\n        \"node\": \"^10.12.0 || >=12.0.0\"\n      }\n    },\n    \"node_modules/flatted\": {\n      \"version\": \"3.4.3\",\n      \"resolved\": \"https://registry.npmmirror.com/flatted/-/flatted-3.4.3.tgz\",\n      \"integrity\": \"sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==\",\n      \"dev\": true,\n      \"license\": \"ISC\"\n    },\n    \"node_modules/fs-constants\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz\",\n      \"integrity\": \"sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/fs.realpath\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz\",\n      \"integrity\": \"sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==\",\n      \"dev\": true,\n      \"license\": \"ISC\"\n    },\n    \"node_modules/fsevents\": {\n      \"version\": \"2.3.2\",\n      \"resolved\": \"https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz\",\n      \"integrity\": \"sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==\",\n      \"hasInstallScript\": true,\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"darwin\"\n      ],\n      \"engines\": {\n        \"node\": \"^8.16.0 || ^10.6.0 || >=11.0.0\"\n      }\n    },\n    \"node_modules/github-from-package\": {\n      \"version\": \"0.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz\",\n      \"integrity\": \"sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/glob\": {\n      \"version\": \"7.2.3\",\n      \"resolved\": \"https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz\",\n      \"integrity\": \"sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==\",\n      \"deprecated\": \"Glob versions prior to v9 are no longer supported\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"fs.realpath\": \"^1.0.0\",\n        \"inflight\": \"^1.0.4\",\n        \"inherits\": \"2\",\n        \"minimatch\": \"^3.1.1\",\n        \"once\": \"^1.3.0\",\n        \"path-is-absolute\": \"^1.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"*\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/isaacs\"\n      }\n    },\n    \"node_modules/glob-parent\": {\n      \"version\": \"6.0.2\",\n      \"resolved\": \"https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz\",\n      \"integrity\": \"sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"is-glob\": \"^4.0.3\"\n      },\n      \"engines\": {\n        \"node\": \">=10.13.0\"\n      }\n    },\n    \"node_modules/globals\": {\n      \"version\": \"13.24.0\",\n      \"resolved\": \"https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz\",\n      \"integrity\": \"sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"type-fest\": \"^0.20.2\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/graphemer\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz\",\n      \"integrity\": \"sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/has-flag\": {\n      \"version\": \"4.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz\",\n      \"integrity\": \"sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/ieee754\": {\n      \"version\": \"1.2.1\",\n      \"resolved\": \"https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz\",\n      \"integrity\": \"sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==\",\n      \"funding\": [\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/feross\"\n        },\n        {\n          \"type\": \"patreon\",\n          \"url\": \"https://www.patreon.com/feross\"\n        },\n        {\n          \"type\": \"consulting\",\n          \"url\": \"https://feross.org/support\"\n        }\n      ],\n      \"license\": \"BSD-3-Clause\"\n    },\n    \"node_modules/ignore\": {\n      \"version\": \"5.3.2\",\n      \"resolved\": \"https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz\",\n      \"integrity\": \"sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 4\"\n      }\n    },\n    \"node_modules/import-fresh\": {\n      \"version\": \"3.3.1\",\n      \"resolved\": \"https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz\",\n      \"integrity\": \"sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"parent-module\": \"^1.0.0\",\n        \"resolve-from\": \"^4.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=6\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/imurmurhash\": {\n      \"version\": \"0.1.4\",\n      \"resolved\": \"https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz\",\n      \"integrity\": \"sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=0.8.19\"\n      }\n    },\n    \"node_modules/inflight\": {\n      \"version\": \"1.0.6\",\n      \"resolved\": \"https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz\",\n      \"integrity\": \"sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==\",\n      \"deprecated\": \"This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"once\": \"^1.3.0\",\n        \"wrappy\": \"1\"\n      }\n    },\n    \"node_modules/inherits\": {\n      \"version\": \"2.0.4\",\n      \"resolved\": \"https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz\",\n      \"integrity\": \"sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==\",\n      \"license\": \"ISC\"\n    },\n    \"node_modules/ini\": {\n      \"version\": \"1.3.8\",\n      \"resolved\": \"https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz\",\n      \"integrity\": \"sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==\",\n      \"license\": \"ISC\"\n    },\n    \"node_modules/is-extglob\": {\n      \"version\": \"2.1.1\",\n      \"resolved\": \"https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz\",\n      \"integrity\": \"sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/is-glob\": {\n      \"version\": \"4.0.3\",\n      \"resolved\": \"https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz\",\n      \"integrity\": \"sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"is-extglob\": \"^2.1.1\"\n      },\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/is-path-inside\": {\n      \"version\": \"3.0.3\",\n      \"resolved\": \"https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz\",\n      \"integrity\": \"sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/isexe\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz\",\n      \"integrity\": \"sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==\",\n      \"dev\": true,\n      \"license\": \"ISC\"\n    },\n    \"node_modules/js-yaml\": {\n      \"version\": \"4.3.0\",\n      \"resolved\": \"https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.0.tgz\",\n      \"integrity\": \"sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==\",\n      \"dev\": true,\n      \"funding\": [\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/puzrin\"\n        },\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/nodeca\"\n        }\n      ],\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"argparse\": \"^2.0.1\"\n      },\n      \"bin\": {\n        \"js-yaml\": \"bin/js-yaml.js\"\n      }\n    },\n    \"node_modules/json-buffer\": {\n      \"version\": \"3.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz\",\n      \"integrity\": \"sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/json-schema-traverse\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz\",\n      \"integrity\": \"sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/json-stable-stringify-without-jsonify\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz\",\n      \"integrity\": \"sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/keyv\": {\n      \"version\": \"4.5.4\",\n      \"resolved\": \"https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz\",\n      \"integrity\": \"sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"json-buffer\": \"3.0.1\"\n      }\n    },\n    \"node_modules/levn\": {\n      \"version\": \"0.4.1\",\n      \"resolved\": \"https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz\",\n      \"integrity\": \"sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"prelude-ls\": \"^1.2.1\",\n        \"type-check\": \"~0.4.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.8.0\"\n      }\n    },\n    \"node_modules/locate-path\": {\n      \"version\": \"6.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz\",\n      \"integrity\": \"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"p-locate\": \"^5.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/lodash.merge\": {\n      \"version\": \"4.6.2\",\n      \"resolved\": \"https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz\",\n      \"integrity\": \"sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/mimic-response\": {\n      \"version\": \"3.1.0\",\n      \"resolved\": \"https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz\",\n      \"integrity\": \"sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/minimatch\": {\n      \"version\": \"3.1.5\",\n      \"resolved\": \"https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz\",\n      \"integrity\": \"sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"brace-expansion\": \"^1.1.7\"\n      },\n      \"engines\": {\n        \"node\": \"*\"\n      }\n    },\n    \"node_modules/minimist\": {\n      \"version\": \"1.2.8\",\n      \"resolved\": \"https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz\",\n      \"integrity\": \"sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==\",\n      \"license\": \"MIT\",\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/mkdirp-classic\": {\n      \"version\": \"0.5.3\",\n      \"resolved\": \"https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz\",\n      \"integrity\": \"sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/ms\": {\n      \"version\": \"2.1.3\",\n      \"resolved\": \"https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz\",\n      \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/napi-build-utils\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz\",\n      \"integrity\": \"sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/natural-compare\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz\",\n      \"integrity\": \"sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/node-abi\": {\n      \"version\": \"3.94.0\",\n      \"resolved\": \"https://registry.npmmirror.com/node-abi/-/node-abi-3.94.0.tgz\",\n      \"integrity\": \"sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"semver\": \"^7.3.5\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      }\n    },\n    \"node_modules/once\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmmirror.com/once/-/once-1.4.0.tgz\",\n      \"integrity\": \"sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"wrappy\": \"1\"\n      }\n    },\n    \"node_modules/optionator\": {\n      \"version\": \"0.9.4\",\n      \"resolved\": \"https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz\",\n      \"integrity\": \"sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"deep-is\": \"^0.1.3\",\n        \"fast-levenshtein\": \"^2.0.6\",\n        \"levn\": \"^0.4.1\",\n        \"prelude-ls\": \"^1.2.1\",\n        \"type-check\": \"^0.4.0\",\n        \"word-wrap\": \"^1.2.5\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.8.0\"\n      }\n    },\n    \"node_modules/p-limit\": {\n      \"version\": \"3.1.0\",\n      \"resolved\": \"https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz\",\n      \"integrity\": \"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"yocto-queue\": \"^0.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/p-locate\": {\n      \"version\": \"5.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz\",\n      \"integrity\": \"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"p-limit\": \"^3.0.2\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/parent-module\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz\",\n      \"integrity\": \"sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"callsites\": \"^3.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=6\"\n      }\n    },\n    \"node_modules/path-exists\": {\n      \"version\": \"4.0.0\",\n      \"resolved\": \"https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz\",\n      \"integrity\": \"sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/path-is-absolute\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz\",\n      \"integrity\": \"sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/path-key\": {\n      \"version\": \"3.1.1\",\n      \"resolved\": \"https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz\",\n      \"integrity\": \"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/playwright\": {\n      \"version\": \"1.61.1\",\n      \"resolved\": \"https://registry.npmmirror.com/playwright/-/playwright-1.61.1.tgz\",\n      \"integrity\": \"sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==\",\n      \"license\": \"Apache-2.0\",\n      \"dependencies\": {\n        \"playwright-core\": \"1.61.1\"\n      },\n      \"bin\": {\n        \"playwright\": \"cli.js\"\n      },\n      \"engines\": {\n        \"node\": \">=18\"\n      },\n      \"optionalDependencies\": {\n        \"fsevents\": \"2.3.2\"\n      }\n    },\n    \"node_modules/playwright-core\": {\n      \"version\": \"1.61.1\",\n      \"resolved\": \"https://registry.npmmirror.com/playwright-core/-/playwright-core-1.61.1.tgz\",\n      \"integrity\": \"sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==\",\n      \"license\": \"Apache-2.0\",\n      \"bin\": {\n        \"playwright-core\": \"cli.js\"\n      },\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/prebuild-install\": {\n      \"version\": \"7.1.2\",\n      \"resolved\": \"https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.2.tgz\",\n      \"integrity\": \"sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"detect-libc\": \"^2.0.0\",\n        \"expand-template\": \"^2.0.3\",\n        \"gi\n\nFile v0.1.0:package.json\n\n{\n  \"name\": \"bidding-hunter\",\n  \"version\": \"1.0.1\",\n  \"description\": \"Automated government procurement bid discovery engine with pluggable platform adapters\",\n  \"main\": \"src/index.js\",\n  \"bin\": {\n    \"bidding-hunter\": \"./bin/bidding-hunter.js\"\n  },\n  \"files\": [\n    \"bin/\",\n    \"src/\",\n    \"config/\",\n    \"templates/\",\n    \"scripts/\",\n    \"README.md\",\n    \"DESIGN.md\",\n    \"LICENSE\"\n  ],\n  \"scripts\": {\n    \"start\": \"node bin/bidding-hunter.js\",\n    \"scan\": \"node bin/bidding-hunter.js scan\",\n    \"test\": \"node --test tests/*.test.js\",\n    \"lint\": \"eslint src/ bin/ tests/\"\n  },\n  \"keywords\": [\n    \"bid\",\n    \"bidding\",\n    \"procurement\",\n    \"government\",\n    \"tender\",\n    \"china\",\n    \"ggzy\",\n    \"automation\",\n    \"playwright\",\n    \"scanner\",\n    \"bid-discovery\"\n  ],\n  \"engines\": {\n    \"node\": \">=18.0.0\"\n  },\n  \"dependencies\": {\n    \"playwright\": \"^1.40.0\",\n    \"better-sqlite3\": \"^11.0.0\",\n    \"yaml\": \"^2.3.0\",\n    \"ajv\": \"^8.12.0\",\n    \"chalk\": \"^5.3.0\",\n    \"commander\": \"^12.0.0\",\n    \"dayjs\": \"^1.11.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.50.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/HuaGavin/bidding-hunter.git\"\n  },\n  \"author\": \"OpenClaw Community\",\n  \"license\": \"MIT\"\n}\n\nFile v0.1.0:.github/workflows/test.yml\n\nname: Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version: [18, 20, 22]\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n      - run: npm install\n      - run: npx playwright install chromium --with-deps\n      - run: npm test\n\nFile v0.1.0:config/default.yaml\n\n# Bidding Hunter — Default Configuration\n# Copy this file to ~/.bidding-hunter/config.yaml and customize.\n# All paths support ~ expansion and ${ENV_VAR} substitution.\n\n# ============================================================\n# Matching Configuration (REQUIRED)\n# ============================================================\nmatching:\n  # Search queries: what to type into platform search boxes.\n  # Each query runs a separate search. REPLACE WITH YOUR OWN KEYWORDS.\n  search_queries: []\n  # Example: [\"video\", \"design\", \"construction\", \"consulting\"]\n\n  # Keyword tiers for title matching. Matched in order (high → medium → low).\n  # An item gets the first tier that matches.\n  tiers:\n    high:\n      label: \"L1\"\n      description: \"High relevance — direct business match\"\n      keywords: []\n      # Example: [\"video production\", \"branding\", \"consulting\"]\n    medium:\n      label: \"L2\"\n      description: \"Medium relevance — related services\"\n      keywords: []\n      # Example: [\"event planning\", \"design\", \"logistics\"]\n    low:\n      label: \"L3\"\n      description: \"Low relevance — extended keywords\"\n      keywords: []\n      # Example: [\"media\", \"printing\", \"translation\"]\n\n  # Blacklist: titles containing any of these words are filtered out.\n  blacklist: []\n  # Example for Chinese procurement: [\"中标\", \"成交\", \"废标\", \"更正\", \"变更\", \"流标\"]\n\n  # Case-sensitive matching (default: false, case-insensitive)\n  case_sensitive: false\n\n# ============================================================\n# Database Configuration\n# ============================================================\ndatabase:\n  # Path to SQLite database file\n  path: \"~/.bidding-hunter/data.db\"\n  # Backup before each scan (will add .bak suffix)\n  backup_before_scan: true\n\n# ============================================================\n# Scan Configuration\n# ============================================================\nscan:\n  # Date window: how many days back to scan (from today)\n  # e.g., 2 = today + yesterday\n  date_window: 2\n\n  # Maximum pages to scan per search query per platform\n  max_pages: 15\n\n  # Maximum concurrent platform scans\n  concurrency: 3\n\n  # Retry strategy for page loads (ms)\n  # Each step: [timeout_ms, wait_until_strategy]\n  retry_stairs:\n    - timeout: 30000\n      wait_until: \"domcontentloaded\"\n    - timeout: 45000\n      wait_until: \"domcontentloaded\"\n    - timeout: 60000\n      wait_until: \"networkidle\"\n\n  # Wait between actions to avoid detection (ms)\n  polite_delay: 2500\n\n  # Output directory for scan results\n  results_dir: \"~/.bidding-hunter/scan_results\"\n\n  # User agent string for browser\n  user_agent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\"\n\n# ============================================================\n# Platforms Configuration\n# ============================================================\nplatforms:\n  # Which platforms to scan. Use platform IDs (see README for built-in list).\n  enabled:\n    - beijing\n    - hebei\n    - liaoning\n    - dalian\n    - national\n\n  # Custom platform adapter paths (in addition to built-in)\n  custom_paths: []\n\n  # Platform-specific overrides\n  overrides:\n    # Example: disable a specific platform temporarily\n    # beijing:\n    #   enabled: false\n\n    # Example: increase max pages for a specific platform\n    # national:\n    #   max_pages: 20\n\n# ============================================================\n# Detail Fetch Configuration\n# ============================================================\ndetail_fetch:\n  # Whether to fetch detail pages for tracked entries\n  enabled: true\n\n  # Max concurrent detail page fetches\n  concurrency: 2\n\n  # Only fetch for entries without deadlines\n  skip_if_has_deadlines: true\n\n  # Don't retry the same URL on the same day\n  skip_today_retry: true\n\n  # Max days since first_seen to attempt fetch (older entries, skip)\n  max_age_days: 30\n\n# ============================================================\n# Reminder Configuration\n# ============================================================\nreminders:\n  # Days before deadline to start reminding\n  urgent_days: 7\n\n  # Include past-due (yesterday) in urgent\n  include_yesterday: true\n\n# ============================================================\n# Notification Configuration\n# ============================================================\nnotifications:\n  channels: []\n  # Example channels:\n  #   - type: feishu\n  #     webhook: \"${FEISHU_WEBHOOK_URL}\"\n  #     secret: \"${FEISHU_SECRET}\"\n  #\n  #   - type: dingtalk\n  #     webhook: \"${DINGTALK_WEBHOOK_URL}\"\n  #     secret: \"${DINGTALK_SECRET}\"\n  #\n  #   - type: slack\n  #     webhook: \"${SLACK_WEBHOOK_URL}\"\n  #\n  #   - type: file\n  #     path: \"~/.bidding-hunter/reports/\"\n  #\n  #   - type: stdout\n  #     format: markdown   # markdown | text | json\n\n# ============================================================\n# Report Configuration\n# ============================================================\nreport:\n  # Max title length in report (longer titles truncated)\n  max_title_length: 60\n\n  # Include scan statistics section\n  show_stats: true\n\n  # Include reminder section\n  show_reminders: true\n\n  # Show undecided count prompt\n  show_undecided_prompt: true\n\n# ============================================================\n# Scheduling (for reference; actual cron config is separate)\n# ============================================================\nschedule:\n  # Cron expression (informational — set up crontab separately)\n  cron: \"0 16 * * *\"\n  timezone: \"Asia/Shanghai\"\n\n# ============================================================\n# Session / Context Management\n# ============================================================\nsessions:\n  # Max tokens before compacting persistent context\n  max_tokens: 60000\n\n# ============================================================\n# Logging\n# ============================================================\nlogging:\n  # Log level: debug | info | warn | error\n  level: \"info\"\n\n  # Log to file\n  file: \"~/.bidding-hunter/bidding-hunter.log\"\n\n  # Log to stderr\n  stderr: true","readmeExcerpt":"Skill: 定制化招标信息采集 Owner: huagavin Summary: Automated government procurement bid discovery engine. Scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and delivers deadline-aware reports. Users configure their own keywords and monitoring targets; built-in adapters cover 5 major platforms with an extensible plugin model for adding more. Tags: latest:0.1.0 Version history: v0","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"Existing tool fails\n  → Read the tool's source code\n  → Fix the bug in the tool itself (edit src/*.js, bin/*.js, scripts/*.js, templates/*.js)\n  → Run tests: node --test tests/*.test.js\n  → Re-run the tool\n  → If the tool is fundamentally broken beyond repair: refactor the tool, record in memory/YYYY-MM-DD.md"},{"language":"bash","snippet":"# Create initial config\nbidding-hunter init\n\n# Edit config with YOUR keywords\n# vim ~/.bidding-hunter/config.yaml\n\n# Run a scan\nbidding-hunter scan\n\n# View results\nbidding-hunter report"},{"language":"yaml","snippet":"matching:\n  search_queries:        # What to type into platform search boxes\n    - \"建筑工程\"\n    - \"信息化\"\n    \n  tiers:                 # Multi-tier keyword matching\n    high:\n      label: \"L1\"\n      keywords: [\"建筑工程\", \"土建施工\", \"信息系统\"]\n    medium:\n      label: \"L2\"  \n      keywords: [\"系统集成\", \"运维服务\"]\n    low:\n      label: \"L3\"\n      keywords: [\"市政工程\", \"园林绿化\"]\n      \n  blacklist:             # Filter these out\n    - \"中标\"\n    - \"成交\"\n    - \"废标\"\n    - \"更正\""},{"language":"yaml","snippet":"platforms:\n  enabled:\n    - beijing    # 北京公共资源交易平台\n    - hebei      # 河北省招标投标公共服务平台\n    - liaoning   # 辽宁省公共资源交易平台\n    - dalian     # 大连市公共资源交易平台\n    - national   # 全国公共资源交易平台 (6 data sources)"},{"language":"bash","snippet":"bidding-hunter scan"},{"language":"bash","snippet":"# List tracked entries\nbidding-hunter list --status tracked\n\n# Mark as tracked\nbidding-hunter status --id 5 --status tracked\n\n# Update bid progress  \nbidding-hunter status --id 5 --bid-status submitted\n\n# Check reminders\nbidding-hunter remind"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: bidding-hunter\ndescription: >-\n  Automated government procurement bid discovery engine. Scans multiple Chinese\n  procurement platforms, matches configurable keywords, tracks bid statuses,\n  and delivers deadline-aware reports. Users configure their own keywords and\n  monitoring targets; built-in adapters cover 5 major platforms with an\n  extensible plugin model for adding more.\nversion: \"1.0.1\"\ntags:\n  - bidding\n  - procurement\n  - government\n  - china\n  - automation\n  - scanner\n  - playwright\n  - bid-discovery\n---\n\n# Bidding Hunter — 投标发掘引擎\n\nA config-driven, reusable bid discovery engine that automates the tedious process of monitoring Chinese government procurement platforms.\n\n**Compatible with any AI assistant that can execute CLI tools:** OpenClaw, Claude Code, Codex CLI, Cursor Agent, and similar platforms. The skill is platform-agnostic — all operations are exposed through the `bidding-hunter` CLI and standard shell commands.\n\n## 🚨 MANDATORY TOOL DISCIPLINE — READ FIRST\n\n**ANY bidding-hunter task MUST use the existing tools below. You are FORBIDDEN from writing ad-hoc scripts, inline Node.js, curl, web_fetch, or any other approach.**\n\n### Existing Tools (the ONLY allowed tools for bidding-hunter work)\n\n| Tool | Purpose | Command |\n|------|---------|---------|\n| `bidding-hunter` CLI | All operations: scan, report, list, status, stats, export, remind, init, create-adapter, test-platform, config-validate | `bidding-hunter <command>` |\n| `scripts/explore-platform.js` | Explore a new platform's structure before writing an adapter | `node scripts/explore-platform.js --url <url> [--recon]` |\n| `templates/platform-adapter.js` | Template for creating new platform adapters | Used by `bidding-hunter create-adapter` |\n| Platform adapters | `src/platforms/*.js` — scan() methods executed by the scanner | Called internally by `bidding-hunter scan` |\n| Core modules | scanner, matcher, database, reporter, detail-fetcher, reminder, notifier | Called internally by the CLI |\n\n### Rules (ZERO EXCEPTIONS)\n\n1. **Use tools, don't replace them.** A bidding-hunter task is a tool-execution task, NOT a coding task.\n2. **No ad-hoc scripts.** Do NOT write `scrape_*.js`, `fetch_*.js`, `test_*.js`, inline `node -e \"...\"`, `curl`, `web_fetch`, or any one-off scraping code.\n3. **No browser shortcuts.** Do NOT use Playwright/Selenium directly unless you are inside a platform adapter's `scan()` method. All browser orchestration is handled by the scanner.\n4. **No manual dedup/matching.** Do NOT copy-paste from the matcher or database modules. Use `bidding-hunter scan` → `bidding-hunter report`.\n\n### Escalation Path (when something breaks)\n\n```\nExisting tool fails\n  → Read the tool's source code\n  → Fix the bug in the tool itself (edit src/*.js, bin/*.js, scripts/*.js, templates/*.js)\n  → Run tests: node --test tests/*.test.js\n  → Re-run the tool\n  → If the tool is fundamentally broken beyond repair: refactor the tool, record in memory/YYYY-MM-DD.md\n```\n\n**Key princi"},{"path":"README.md","content":"# 投标猎手 🎯\n\n> 每天自动帮你盯着各大政府采购平台，发现匹配的招标机会就通知你。  \n> 不用一个个网站去翻，不用怕错过截止日期——它帮你干这些重复活儿。\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)\n\n---\n\n## 这是什么？\n\n如果你或者你的公司需要经常找政府采购项目去投标，你一定经历过这些事：\n\n- 每天打开好几个政府采购网站，一个一个搜关键词\n- 搜出来的结果里混杂着中标公告、更正公告、废标公告，得人工过滤\n- 记下项目名称、截止日期、预算金额，生怕漏了哪一个\n- 不同省份的网站格式还不一样，换个省份又要重新适应\n\n**投标猎手就是帮你干这些事的。** 它自动访问你关心的政府采购平台，按照你设置的关键词筛选招标公告，过滤掉无关信息，把结果整理好汇报给你。\n\n### 它具体能做什么\n\n1. **自动扫描** —— 定时访问北京、河北、辽宁、大连、全国等政府采购平台，不用你手动打开浏览器\n2. **关键词匹配** —— 你告诉它关心什么业务（比如\"建筑工程\"\"信息系统\"），它按重要程度分三级筛选\n3. **过滤噪音** —— 自动跳过中标公告、废标公告、更正公告等跟你没关系的内容\n4. **提取关键信息** —— 自动从公告详情页提取投标截止时间、开标时间、预算金额\n5. **跟踪进度** —— 记录每个项目的状态（关注中 → 已投标 → 中标/未中标），全程可追溯\n6. **到期提醒** —— 快截止的项目主动提醒你，不会因为忙忘了投\n7. **通知推送** —— 扫描结果可以发到飞书、钉钉、企微，或者导出文件\n\n---\n\n## 谁适合用？\n\n- 📋 **每天需要找投标机会的商务、销售、市场人员** —— 不用再手动翻网页\n- 🏢 **投标团队的办公室文员** —— 一个人盯几个省份的标，效率翻倍\n- 🤖 **有 AI 助手的人**（OpenClaw、Claude Code 等）—— 可以让 AI 帮你管理和分析投标数据\n\n---\n\n## 怎么装？\n\n### 第一步：装 Node.js\n\n投标猎手需要 Node.js 运行环境。如果你的电脑上已经有 Node.js（版本 ≥ 18），跳过这一步。\n\n**没装过的话**，去 [nodejs.org](https://nodejs.org) 下载安装包，选 LTS 版本（左边的按钮），像装普通软件一样双击安装就行。\n\n装好后打开终端（Windows 上是 PowerShell 或 CMD，Mac 上是终端），输入下面命令确认装好了：\n\n```bash\nnode --version\n# 应该显示类似 v18.x.x 或 v20.x.x\n```\n\n### 第二步：装投标猎手\n\n在终端里输入：\n\n```bash\nnpm install -g bidding-hunter\n```\n\n> 💡 `npm install -g` 的意思是\"全局安装\"，装一次就行，之后在任何目录都能用。\n\n### 第三步：装浏览器驱动\n\n投标猎手需要一个自动化浏览器来访问网页，运行下面命令：\n\n```bash\nnpx playwright install chromium\n```\n\n> 💡 这个命令会下载一个 Chromium 浏览器（Chrome 的开源版本），大约 150MB。只需要运行一次。\n\n### 第四步：创建配置文件\n\n```bash\nbidding-hunter init\n```\n\n这个命令会在你的电脑上创建 `~/.bidding-hunter/config.yaml` 配置文件。接下来你需要编辑它，告诉投标猎手你关心什么关键词。\n\n---\n\n## 配置你的关键词\n\n配置文件是最重要的——你告诉投标猎手你要找什么，它就帮你找什么。\n\n用任意文本编辑器打开 `~/.bidding-hunter/config.yaml`（Windows 上路径是 `C:\\Users\\你的用户名\\.bidding-hunter\\config.yaml`）：\n\n```yaml\nmatching:\n  # 搜索词：在每个平台搜索框里输入什么\n  # 一个搜索词就是一次搜索，建议 2-4 个，太多反而慢\n  search_queries:\n    - \"建筑工程\"\n    - \"信息化\"\n\n  # 关键词分级：匹配标题时按优先级筛选\n  tiers:\n    high:                          # 第一优先级：核心业务\n      label: \"重点项目\"\n      keywords: [\"建筑工程\", \"土建施工\", \"信息系统\"]\n    medium:                        # 第二优先级：相关业务\n      label: \"一般项目\"\n      keywords: [\"系统集成\", \"运维服务\", \"弱电工程\"]\n    low:                           # 第三优先级：拓展业务\n      label: \"关注项目\"\n      keywords: [\"市政工程\", \"园林绿化\", \"物业管理\"]\n\n  # 黑名单：标题里包含这些词的就跳过\n  # 这些都是\"已经结束/不相关\"的信号\n  blacklist:\n    - \"中标\"\n    - \"成交\"\n    - \"废标\"\n    - \"更正\"\n    - \"流标\"\n    - \"终止\"\n    - \"澄清\"\n\n# 要扫描哪些平台\nplatforms:\n  enabled:\n    - beijing        # 北京公共资源交易平台\n    - hebei          # 河北省招标投标公共服务平台\n    - liaoning       # 辽宁省公共资源交易平台\n    - dalian         # 大连市公共资源交易平台\n    - national       # 全国公共资源交易平台（覆盖最广）\n```\n\n> ⚠️ **关键词怎么选？** 建议用你的核心业务词汇，2-4 个字一组，不要太长。比如你是做弱电工程的，用\"弱电\"\"安防\"\"智能化\"就比\"弱电系统工程设计与施工\"效果好——搜索词越短，命中率越高。\n\n---\n\n## 日常使用\n\n### 每天扫一次\n\n```bash\nbidding-hunter scan\n```\n\n运行后它会逐个打开你配置的平台，搜索关键词，筛选匹配的公告。扫描完会自动保存结果。\n\n整个过程大概 2-5 分钟（取决于开了几个平台），你可以在终端看到实时进度。\n\n### 看今天的报告\n\n```bash\nbidding-hunter report --today\n```\n\n输出类似这样：\n\n```\n📊 投标猎手日报 — 2026-07-23\n\n───────────────"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn727b0hk04906tj8vp8kzrb3x8763rk\",\n  \"slug\": \"bidding-hunter\",\n  \"version\": \"0.1.0\",\n  \"publishedAt\": 1786283866145\n}"},{"path":"CHANGELOG.md","content":"# Changelog\n\nAll notable changes to Bidding Hunter will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [1.0.1] - 2026-07-22\n\n### Fixed\n- Critical: Added missing `database.ingest()` function to both SQLite and JSON backends\n- Critical: YAML `wait_until` silently ignored by `gotoWithRetry` (snake_case ↔ camelCase mismatch) — added normalization in config.js\n- Critical: `/home/$USER` literal string bug in 3 files (database.js, lock.js, notifier.js) — replaced with `path.join('/home', process.env.USER)`\n- Warning: `updates.note` vs `updates.notes` inconsistency in SQLite history recording\n- Warning: `package.json` repository URL was placeholder (`user/bidding-hunter`)\n- Warning: Example keywords in README.md exposed business domain — replaced with generic construction procurement terms\n- Warning: Platform-specific URL transformation in detail-fetcher.js extracted to adapter `transformDetailUrl()` hook\n- Warning: national.js DATA_SOURCES reordered 1→6 to sequential 1→2→3→4→5→6\n- Warning: hebei.js and liaoning.js had hardcoded default query `['视频']` — removed, errors instead\n- Info: SKILL.md example keywords synced with README (generic construction/IT terms)\n- Info: `fetchDetails` builds URL transform closure once (was O(n×m) adapter loading)\n- Info: CLI `fetchDetail` falls back to registry for URL transform when no options passed\n- Info: database.js `new Database()` wrapped in try/catch for graceful fallback when native binding missing\n- Info: `notifier.js` uses centralized `resolvePath()` instead of inline regex\n- Info: `DATE_RE` global regex state protected with try-finally\n- Reporter: Skip empty scan stats line when no stats available\n\n### Changed\n- SKILL.md: Added mandatory tool-discipline section (agents must use existing tools, not write ad-hoc scripts)\n- SKILL.md: Added platform compatibility note (works with any AI assistant that can execute CLI tools)\n- BasePlatformAdapter: Added `transformDetailUrl(url)` hook for platform-specific URL transformations\n- Registry: Added `findTransformDetailUrl()` for adapter URL transform lookup\n- Scanner: Null element tracking and filtering during item collection\n- Scanner: `politeDelay` from config passed to adapterContext (was hardcoded per-adapter)\n- All platform adapters: use `ctx.politeDelay` instead of hardcoded `waitForTimeout` values\n\n## [1.0.0] - 2026-07-22\n\n### Added\n- Core scanning engine with pluggable platform adapter system\n- 5 built-in platform adapters: Beijing, Hebei, Liaoning, Dalian, National\n- Configurable multi-tier keyword matching engine\n- SQLite database with JSON fallback for storage\n- Detail page fetcher with automatic date/budget/method extraction\n- Deadline tracking and reminder engine (urgent/open-results/missing-dates)\n- Multi-channel notification system (stdout, file, webhook, Feishu, DingTalk, Slack)\n- Markdown and JSON repor"},{"path":"CONTRIBUTING.md","content":"# Contributing to Bidding Hunter\n\nThanks for your interest in contributing! This project automates government procurement bid discovery — every new platform adapter helps more people find relevant opportunities.\n\n## Ways to Contribute\n\n### 🔌 Add a New Platform Adapter\n\nThis is the most valuable contribution! Adding support for a new regional procurement platform.\n\n1. **Check existing platforms**: See `src/platforms/` for examples\n2. **Use the exploration tool**: `node scripts/explore-platform.js --url <url> --recon`\n3. **Create from template**: `bidding-hunter create-adapter --name <id>`\n4. **Implement the adapter**:\n   - `meta` — Platform metadata\n   - `scan(context, config)` — Main scanning logic\n   - `extractItems(page, { today, fromDate })` — DOM extraction\n5. **Test**: `bidding-hunter test-platform --name <id>`\n6. **Submit PR** with:\n   - The adapter file in `src/platforms/`\n   - Updated README platform list\n   - Brief description of the platform\n\n### 🐛 Bug Reports\n\n- Check existing issues first\n- Include: platform name, error message, steps to reproduce\n- If possible, include browser console output or page structure changes\n\n### 💡 Feature Requests\n\nOpen an issue with:\n- What you want to achieve\n- Why the current system doesn't support it\n- How you envision it working\n\n### 🧪 Testing\n\n```bash\n# Run unit tests\nnpm test\n\n# Test a specific platform\nbidding-hunter test-platform --name <id>\n\n# Run a dry-run scan\nbidding-hunter scan --dry-run\n```\n\n## Development Setup\n\n```bash\ngit clone https://github.com/user/bidding-hunter.git\ncd bidding-hunter\nnpm install\nnpx playwright install chromium\n\n# Create test config\nbidding-hunter init\n```\n\n## Platform Adapter Guidelines\n\nA good platform adapter:\n\n1. **Is defensive**: Handles network timeouts, changed selectors, no-data states\n2. **Uses retry stairs**: 30s → 45s → 60s with `gotoWithRetry`\n3. **Respects rate limits**: Uses delays between actions\n4. **Extracts clean data**: Returns `{ site, region, title, date, url }`\n5. **Is self-contained**: One file, no external dependencies beyond Playwright\n6. **Has clear logging**: Console messages in `site [query]` format\n7. **Handles pagination**: Button-based or URL-based with max page limit\n8. **Filters by date window**: Only returns items within `fromDate ~ today`\n\n### Adapter Checklist\n\n- [ ] `meta.id` is lowercase, no spaces, unique\n- [ ] `meta.url` points to the listing page (not detail page)\n- [ ] `scan()` creates its own browser context + page\n- [ ] Uses `this.gotoWithRetry()` for navigation\n- [ ] `extractItems()` uses `page.$$eval()` with serialized options\n- [ ] Date extraction handles multiple formats (YYYY-MM-DD, YYYY/MM/DD, etc.)\n- [ ] Returns items with all required fields\n- [ ] Closes browser context in `finally` block\n- [ ] Tested with `bidding-hunter test-platform`\n\n## Code Style\n\n- ES6+ with `'use strict'`\n- 2-space indentation\n- Single quotes for strings (except template literals)\n- `async/await` for all async operations\n- Descriptive variab"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Automated government procurement bid discovery engine. Scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and delivers deadline-aware reports. Users configure their own keywords and monitoring targets; built-in adapters cover 5 major platforms with an extensible plugin model for adding more. Skill: 定制化招标信息采集 Owner: huagavin Summary: Automated government procurement bid discovery engine. Scans multiple Chinese procurement platforms, matches configurable keywords, tracks bid statuses, and delivers deadline-aware reports. Users configure their own keywords and monitoring targets; built-in adapters cover 5 major platforms with an extensible plugin model for adding more. Tags: latest:0.1.0 Version history: v0","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1071,"uniquenessScore":54,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T17:57:25.890Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T17:57:25.890Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:40:14.303Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}