This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
# OpenCLI Integration - 通用CLI集成技能
集成 [OpenCLI](https://github.com/jackwener/opencli) 能力,将任何网站、Electron应用转换为命令行接口。
## 🎯 项目定位
这是**预备技能**,当前保持 **xhs-cli + lightpanda** 方案(稳定),但为未来扩展保留OpenCLI能力。
```
当前方案(稳定) 未来扩展(预留)
├─ xhs-cli ├─ OpenCLI ← 本技能
├─ lightpanda └─ 更多网站CLI化
└─ 现有爬虫
```
## 🚀 快速开始
### 安装OpenCLI
```bash
# 1. 安装Node.js >= 20.0.0
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# 2. 安装OpenCLI
npm install -g @jackwener/opencli
# 3. 验证安装
opencli --version
```
### 基础使用
```bash
# 检查状态
python3 opencli_adapter.py status
# 诊断环境
python3 opencli_adapter.py doctor
# 探索网站API
python3 opencli_adapter.py explore https://www.xiaohongshu.com
# 生成适配器
python3 opencli_adapter.py synthesize xiaohongshu
# 执行命令
python3 opencli_adapter.py exec xiaohongshu search --args query=关键词
```
## 📚 API文档
### 初始化
```python
from opencli_adapter import OpenCLIAdapter
adapter = OpenCLIAdapter()
```
### 核心方法
| 方法 | 说明 | 示例 |
|------|------|------|
| `setup()` | 初始化环境 | `adapter.setup()` |
| `doctor()` | 诊断状态 | `adapter.doctor()` |
| `list()` | 列出适配器 | `adapter.list()` |
| `explore(url)` | 探索API | `adapter.explore("https://...")` |
| `synthesize(name)` | 生成适配器 | `adapter.synthesize("mysite")` |
| `execute(cli, action)` | 执行命令 | `adapter.execute("xhs", "search", query="...")` |
### 便捷方法
```python
# 搜索
adapter.search("xiaohongshu", "关键词")
# 获取详情
adapter.get("xiaohongshu", "note_id")
# 控制Electron应用
adapter.control_electron("antigravity", "open_file", path="/path/to/file")
```
## 🔧 适配器配置示例
### YAML声明式适配器
```python
config = {
"name": "mysite",
"base_url": "https://example.com",
"selectors": {
"search_box": "input[name='q']",
"search_button": "button[type='submit']",
"result_item": ".result-item"
},
"commands": {
"search": {
"description": "搜索内容",
"params": ["query"]
}
}
}
path = adapter.create_yaml_adapter("mysite", config)
```
## 📋 与现有方案对比
| 特性 | xhs-cli | OpenCLI | 适用场景 |
|------|---------|---------|---------|
| 稳定性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 生产环境用xhs-cli |
| 通用性 | ⭐⭐ | ⭐⭐⭐⭐⭐ | 新网站用OpenCLI探索 |
| 易用性 | ⭐⭐⭐⭐ | ⭐⭐⭐ | xhs-cli更直接 |
| 扩展性 | ⭐⭐ | ⭐⭐⭐⭐⭐ | OpenCLI可扩展任意网站 |
| 资源占用 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | xhs-cli更轻量 |
## ⚠️ 使用建议
### 当前阶段(保持现状)
- ✅ 继续使用 `xhs-cli` 处理小红书
- ✅ 继续使用 `lightpanda` 进行网页抓取
- ✅ 本技能作为**技术储备**
### 未来可能的使用场景
1. **新网站接入** - 快速探索并生成CLI
2. **Electron应用** - 控制Antigravity等工具
3. **复杂交互** - 需要浏览器自动化的场景
4. **AI自发现** - 让AI自动学习和使用新工具
## 🔗 参考链接
- OpenCLI GitHub: https://github.com/jackwener/opencli
- npm包: https://www.npmjs.com/package/@jackwener/opencli
- 中文文档: https://github.com/jackwener/opencli/blob/main/README.zh-CN.md
## 📝 License
MIT
+105
View File
@@ -0,0 +1,105 @@
# OpenCLI Integration - 通用CLI集成技能
集成 OpenCLI 能力,将任何网站、Electron应用转换为命令行接口,供AI Agent调用。
## 🎯 功能特性
- ✅ **网站CLI化** - 将任意网站转换为CLI工具
- ✅ **Electron应用控制** - 控制Antigravity等Electron应用
- ✅ **AI自动发现** - 自动探索网站API并生成适配器
- ✅ **浏览器复用** - 复用Chrome登录态,零风控
- ✅ **动态适配器** - 支持.yaml声明式和.ts编程式适配器
## 🚀 使用方法
### 前置要求
```bash
# 1. 安装Node.js >= 20.0.0
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# 2. 安装OpenCLI
npm install -g @jackwener/opencli
# 3. 确保Chrome正在运行且已登录目标网站
```
### 命令行使用
```bash
# 探索网站API
python3 /home/ubuntu/.nanobot/workspace/skills/opencli-integration/opencli_adapter.py explore https://www.xiaohongshu.com
# 生成适配器
python3 /home/ubuntu/.nanobot/workspace/skills/opencli-integration/opencli_adapter.py synthesize xiaohongshu
# 执行CLI命令
python3 /home/ubuntu/.nanobot/workspace/skills/opencli-integration/opencli_adapter.py exec xiaohongshu search "关键词"
# 诊断状态
python3 /home/ubuntu/.nanobot/workspace/skills/opencli-integration/opencli_adapter.py doctor
```
### Python API
```python
from opencli_adapter import OpenCLIAdapter
adapter = OpenCLIAdapter()
# 探索网站
result = adapter.explore("https://www.xiaohongshu.com")
# 生成适配器
adapter.synthesize("xiaohongshu")
# 执行命令
result = adapter.execute("xiaohongshu", "search", query="关键词")
```
## 📋 内置支持
OpenCLI官方支持的网站/应用:
- B站 (bilibili)
- 知乎 (zhihu)
- 小红书 (xiaohongshu)
- Twitter/X
- Reddit
- YouTube
- Antigravity (Electron)
## 🔧 高级用法
### 自定义适配器
```python
# 创建自定义适配器
adapter.create_custom_adapter(
name="mysite",
base_url="https://example.com",
selectors={
"search_box": "input[name='q']",
"search_button": "button[type='submit']"
}
)
```
### Electron应用控制
```python
# 控制Electron应用
adapter.control_electron("antigravity", "open_file", path="/path/to/file")
```
## ⚠️ 注意事项
- 需要Chrome浏览器正在运行
- 需要Node.js >= 20.0.0
- 新网站可能需要手动登录
- AI发现API的准确性需要验证
## 📚 参考
- OpenCLI GitHub: https://github.com/jackwener/opencli
- npm包: https://www.npmjs.com/package/@jackwener/opencli
@@ -0,0 +1,427 @@
#!/usr/bin/env python3
"""
OpenCLI Integration Adapter
将OpenCLI能力封装为Python接口
"""
import subprocess
import json
import os
import shutil
from pathlib import Path
from typing import Dict, List, Optional, Any
class OpenCLIAdapter:
"""OpenCLI适配器"""
def __init__(self, clis_dir: Optional[str] = None):
"""
初始化OpenCLI适配器
Args:
clis_dir: 适配器存储目录,默认 ~/.opencli/clis
"""
self.clis_dir = clis_dir or os.path.expanduser("~/.opencli/clis")
self._check_opencli()
def _check_opencli(self) -> bool:
"""检查OpenCLI是否已安装"""
if not shutil.which("opencli"):
raise RuntimeError(
"OpenCLI未安装。请先运行: npm install -g @jackwener/opencli"
)
return True
def _run_command(self, cmd: List[str], capture_output: bool = True) -> Dict[str, Any]:
"""
运行OpenCLI命令
Args:
cmd: 命令列表
capture_output: 是否捕获输出
Returns:
{"success": bool, "stdout": str, "stderr": str, "returncode": int}
"""
try:
result = subprocess.run(
["opencli"] + cmd,
capture_output=capture_output,
text=True,
timeout=120
)
return {
"success": result.returncode == 0,
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode
}
except subprocess.TimeoutExpired:
return {
"success": False,
"stdout": "",
"stderr": "Command timeout",
"returncode": -1
}
except Exception as e:
return {
"success": False,
"stdout": "",
"stderr": str(e),
"returncode": -1
}
# ==================== 核心功能 ====================
def setup(self) -> Dict[str, Any]:
"""
初始化OpenCLI环境
检查Browser Bridge连通性
"""
return self._run_command(["setup"])
def doctor(self) -> Dict[str, Any]:
"""
诊断OpenCLI状态
检查daemon、扩展和浏览器连接
"""
return self._run_command(["doctor"])
def list(self) -> List[Dict[str, Any]]:
"""
列出所有可用的CLI适配器
Returns:
适配器列表
"""
result = self._run_command(["list", "--json"])
if result["success"]:
try:
return json.loads(result["stdout"])
except json.JSONDecodeError:
return []
return []
def explore(self, url: str, output_dir: Optional[str] = None) -> Dict[str, Any]:
"""
探索网站API
AI自动发现网站接口
Args:
url: 目标网站URL
output_dir: 输出目录
Returns:
探索结果
"""
cmd = ["explore", url]
if output_dir:
cmd.extend(["--output", output_dir])
return self._run_command(cmd)
def synthesize(self, name: str, url: Optional[str] = None) -> Dict[str, Any]:
"""
生成适配器
基于探索结果生成CLI适配器
Args:
name: 适配器名称
url: 网站URL(如已explore可省略)
Returns:
生成结果
"""
cmd = ["synthesize", name]
if url:
cmd.extend(["--url", url])
return self._run_command(cmd)
def register(self, name: str, adapter_path: Optional[str] = None) -> Dict[str, Any]:
"""
注册适配器
Args:
name: 适配器名称
adapter_path: 适配器文件路径
Returns:
注册结果
"""
cmd = ["register", name]
if adapter_path:
cmd.append(adapter_path)
return self._run_command(cmd)
def execute(self, cli_name: str, command: str, **kwargs) -> Dict[str, Any]:
"""
执行CLI命令
Args:
cli_name: CLI名称
command: 命令名称
**kwargs: 命令参数
Returns:
执行结果
"""
cmd = [cli_name, command]
# 添加参数
for key, value in kwargs.items():
if isinstance(value, bool) and value:
cmd.append(f"--{key}")
else:
cmd.extend([f"--{key}", str(value)])
return self._run_command(cmd)
# ==================== 便捷方法 ====================
def search(self, cli_name: str, query: str, **kwargs) -> Dict[str, Any]:
"""
便捷搜索方法
Args:
cli_name: CLI名称
query: 搜索关键词
**kwargs: 其他参数
Returns:
搜索结果
"""
return self.execute(cli_name, "search", query=query, **kwargs)
def get(self, cli_name: str, id: str, **kwargs) -> Dict[str, Any]:
"""
便捷获取方法
Args:
cli_name: CLI名称
id: 资源ID
**kwargs: 其他参数
Returns:
获取结果
"""
return self.execute(cli_name, "get", id=id, **kwargs)
# ==================== Electron应用控制 ====================
def control_electron(self, app_name: str, action: str, **kwargs) -> Dict[str, Any]:
"""
控制Electron应用
Args:
app_name: 应用名称
action: 动作名称
**kwargs: 动作参数
Returns:
执行结果
"""
return self.execute(f"electron:{app_name}", action, **kwargs)
# ==================== 适配器管理 ====================
def create_yaml_adapter(self, name: str, config: Dict[str, Any]) -> str:
"""
创建YAML声明式适配器
Args:
name: 适配器名称
config: 适配器配置
Returns:
适配器文件路径
"""
import yaml
adapter_path = os.path.join(self.clis_dir, f"{name}.yaml")
os.makedirs(self.clis_dir, exist_ok=True)
with open(adapter_path, 'w', encoding='utf-8') as f:
yaml.dump(config, f, allow_unicode=True)
return adapter_path
def get_adapter_info(self, name: str) -> Optional[Dict[str, Any]]:
"""
获取适配器信息
Args:
name: 适配器名称
Returns:
适配器信息
"""
# 检查YAML适配器
yaml_path = os.path.join(self.clis_dir, f"{name}.yaml")
if os.path.exists(yaml_path):
import yaml
with open(yaml_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
# 检查TypeScript适配器
ts_path = os.path.join(self.clis_dir, f"{name}.ts")
if os.path.exists(ts_path):
return {"type": "typescript", "path": ts_path}
return None
def remove_adapter(self, name: str) -> bool:
"""
删除适配器
Args:
name: 适配器名称
Returns:
是否成功
"""
removed = False
yaml_path = os.path.join(self.clis_dir, f"{name}.yaml")
if os.path.exists(yaml_path):
os.remove(yaml_path)
removed = True
ts_path = os.path.join(self.clis_dir, f"{name}.ts")
if os.path.exists(ts_path):
os.remove(ts_path)
removed = True
return removed
# ==================== 状态检查 ====================
def is_ready(self) -> Dict[str, Any]:
"""
检查OpenCLI是否就绪
Returns:
状态信息
"""
checks = {
"opencli_installed": shutil.which("opencli") is not None,
"node_version": None,
"chrome_running": False,
"clis_dir_exists": os.path.exists(self.clis_dir),
}
# 检查Node版本
try:
result = subprocess.run(
["node", "--version"],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
checks["node_version"] = result.stdout.strip()
except:
pass
# 检查Chrome是否运行(简单检查)
try:
result = subprocess.run(
["pgrep", "-x", "chrome"],
capture_output=True,
timeout=2
)
checks["chrome_running"] = result.returncode == 0
except:
pass
checks["ready"] = (
checks["opencli_installed"] and
checks["node_version"] and
checks["clis_dir_exists"]
)
return checks
# ==================== 命令行接口 ====================
def main():
"""命令行入口"""
import argparse
parser = argparse.ArgumentParser(description='OpenCLI Adapter')
subparsers = parser.add_subparsers(dest='command', help='可用命令')
# setup
subparsers.add_parser('setup', help='初始化OpenCLI')
# doctor
subparsers.add_parser('doctor', help='诊断OpenCLI状态')
# list
subparsers.add_parser('list', help='列出可用适配器')
# explore
explore_parser = subparsers.add_parser('explore', help='探索网站API')
explore_parser.add_argument('url', help='目标网站URL')
# synthesize
synth_parser = subparsers.add_parser('synthesize', help='生成适配器')
synth_parser.add_argument('name', help='适配器名称')
synth_parser.add_argument('--url', help='网站URL')
# exec
exec_parser = subparsers.add_parser('exec', help='执行CLI命令')
exec_parser.add_argument('cli', help='CLI名称')
exec_parser.add_argument('action', help='动作名称')
exec_parser.add_argument('--args', nargs='*', help='额外参数')
# status
subparsers.add_parser('status', help='检查状态')
args = parser.parse_args()
if not args.command:
parser.print_help()
return
adapter = OpenCLIAdapter()
if args.command == 'setup':
result = adapter.setup()
print(result["stdout"] if result["success"] else result["stderr"])
elif args.command == 'doctor':
result = adapter.doctor()
print(result["stdout"] if result["success"] else result["stderr"])
elif args.command == 'list':
adapters = adapter.list()
print(json.dumps(adapters, indent=2, ensure_ascii=False))
elif args.command == 'explore':
print(f"正在探索: {args.url}")
result = adapter.explore(args.url)
print(result["stdout"] if result["success"] else result["stderr"])
elif args.command == 'synthesize':
print(f"正在生成适配器: {args.name}")
result = adapter.synthesize(args.name, args.url)
print(result["stdout"] if result["success"] else result["stderr"])
elif args.command == 'exec':
print(f"执行: {args.cli} {args.action}")
result = adapter.execute(args.cli, args.action)
print(result["stdout"] if result["success"] else result["stderr"])
elif args.command == 'status':
status = adapter.is_ready()
print(json.dumps(status, indent=2, ensure_ascii=False))
if __name__ == '__main__':
main()
+145
View File
@@ -0,0 +1,145 @@
#!/usr/bin/env python3
"""
OpenCLI Integration 测试脚本
"""
import sys
sys.path.insert(0, '/home/ubuntu/.nanobot/workspace/skills/opencli-integration')
from opencli_adapter import OpenCLIAdapter
def test_status():
"""测试状态检查"""
print("=" * 60)
print("测试1: 状态检查")
print("=" * 60)
try:
adapter = OpenCLIAdapter()
status = adapter.is_ready()
print(f"OpenCLI安装: {'✅' if status['opencli_installed'] else '❌'}")
print(f"Node版本: {status['node_version'] or '❌ 未安装'}")
print(f"Chrome运行: {'✅' if status['chrome_running'] else '❌'}")
print(f"CLIs目录: {'✅' if status['clis_dir_exists'] else '❌'}")
print(f"整体就绪: {'✅' if status['ready'] else '❌'}")
except RuntimeError as e:
print(f"⚠️ {e}")
print()
def test_adapter_info():
"""测试适配器信息"""
print("=" * 60)
print("测试2: 适配器管理")
print("=" * 60)
try:
adapter = OpenCLIAdapter()
# 检查clis目录
import os
clis_dir = os.path.expanduser("~/.opencli/clis")
if os.path.exists(clis_dir):
adapters = os.listdir(clis_dir)
print(f"已安装适配器: {len(adapters)}个")
for a in adapters[:5]: # 只显示前5个
print(f" - {a}")
else:
print("适配器目录不存在")
except Exception as e:
print(f"⚠️ {e}")
print()
def test_yaml_adapter():
"""测试创建YAML适配器"""
print("=" * 60)
print("测试3: YAML适配器创建")
print("=" * 60)
try:
adapter = OpenCLIAdapter()
config = {
"name": "test_site",
"base_url": "https://example.com",
"description": "测试适配器",
"commands": {
"search": {
"description": "搜索",
"params": ["query"]
}
}
}
path = adapter.create_yaml_adapter("test_site", config)
print(f"✅ 适配器创建成功: {path}")
# 读取验证
info = adapter.get_adapter_info("test_site")
if info:
print(f"✅ 适配器信息读取成功")
print(f" 名称: {info.get('name')}")
print(f" URL: {info.get('base_url')}")
# 清理
adapter.remove_adapter("test_site")
print("✅ 测试适配器已清理")
except Exception as e:
print(f"⚠️ {e}")
print()
def test_list_adapters():
"""测试列出适配器"""
print("=" * 60)
print("测试4: 列出适配器")
print("=" * 60)
try:
adapter = OpenCLIAdapter()
adapters = adapter.list()
if adapters:
print(f"发现 {len(adapters)} 个适配器:")
for a in adapters[:5]:
print(f" - {a.get('name', 'unknown')}")
else:
print("暂无适配器或未安装OpenCLI")
except Exception as e:
print(f"⚠️ {e}")
print()
def main():
"""运行所有测试"""
print("\n")
print("╔" + "=" * 58 + "╗")
print("║" + " " * 12 + "OpenCLI Integration 测试" + " " * 22 + "║")
print("╚" + "=" * 58 + "╝")
print("\n")
test_status()
test_adapter_info()
test_yaml_adapter()
test_list_adapters()
print("=" * 60)
print("所有测试完成!")
print("=" * 60)
print("\n注意: 完整功能测试需要安装OpenCLI和Chrome浏览器")
print("安装命令: npm install -g @jackwener/opencli")
if __name__ == '__main__':
main()