api-provider-status
查询 API 供应商的余额、用量与可用状态,支持多供应商监控与定时汇报。
Install
npx skills add https://github.com/aAAaqwq/AGI-Super-Team/tree/main/skills/api-provider-status
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install aaaaqwq-agi-super-team@llmmart
git clone https://github.com/aAAaqwq/AGI-Super-Team.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole aaaaqwq/agi-super-team collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
API Provider Status Skill
API 供应商状态查询与余额监控。
功能
- 查询各 API 供应商的余额/用量
- 生成统计报告
- 支持定时自动查询
支持的供应商
| 供应商 | 查询方式 | 登录状态存储 | 状态 |
|---|---|---|---|
| - | ❌ 已废弃 | ||
| Provider-B | Playwright / Browser Tool | ~/.playwright-data/provider-b |
✅ 可用 |
| GitHub Copilot | Playwright 抓取 | ~/.playwright-data/github |
✅ 可用 |
| Provider-A (your-provider) | Playwright 抓取 | ~/.playwright-data/your-provider |
✅ 可用 |
| OpenRouter VIP | API 暂不支持 | - | ⏳ |
| ZAI (智谱) | 无公开 API,不查询 | - | ⏭️ 跳过 |
查询方法详解
1. Provider-B
方式: Browser Tool 或 Playwright 抓取
查询页面: https://ai.9w7.cn/console
登录状态: ~/.playwright-data/<provider>/model
抓取数据:
- 当前余额 (¥)
- 历史消耗 (¥)
- 请求次数
- 统计 Tokens
- 统计额度 (¥)
- 平均 RPM / TPM
使用 Browser Tool 查询:
- 打开
https://ai.9w7.cn/console(openclaw browser 已保存登录状态) - 从页面 snapshot 中提取数据看板信息
2. GitHub Copilot Pro
方式: Playwright 抓取(需要先登录)
查询页面: https://github.com/settings/copilot
登录流程:
from playwright.sync_api import sync_playwright
import os
user_data_dir = os.path.expanduser('~/.playwright-data/github')
os.makedirs(user_data_dir, exist_ok=True)
with sync_playwright() as p:
context = p.chromium.launch_persistent_context(
user_data_dir,
headless=False, # 显示浏览器让用户登录
)
page = context.pages[0] if context.pages else context.new_page()
page.goto('https://github.com/settings/copilot')
# 用户手动登录后,登录状态会保存到 user_data_dir
抓取数据:
- Premium requests 用量百分比
- 订阅状态 (Active/Inactive)
正则匹配:
import re
usage_match = re.search(r'Premium requests\s*([\d.]+)%', text)
is_active = 'Copilot Pro is active' in text
3. Provider-A (your-provider)
方式: Playwright 抓取(需要先登录)
查询页面: https://your-provider.example.com/console
登录流程:
user_data_dir = os.path.expanduser('~/.playwright-data/your-provider')
# 同上,使用 launch_persistent_context
抓取数据:
- 当前余额
- 历史消耗
- 请求次数
- 总 Tokens
正则匹配:
balance_match = re.search(r'当前余额💰([\d.]+)', text)
consumed_match = re.search(r'历史消耗💰([\d.]+)', text)
requests_match = re.search(r'请求次数(\d+)', text)
tokens_match = re.search(r'总Tokens(\d+)', text)
使用方法
命令行
# 生成完整报告
python3 ~/clawd/skills/api-provider-status/balance_checker.py report
# 查询指定供应商
python3 ~/clawd/skills/api-provider-status/balance_checker.py query anapi
python3 ~/clawd/skills/api-provider-status/balance_checker.py query github-copilot
python3 ~/clawd/skills/api-provider-status/balance_checker.py query your-provider
# JSON 格式查询所有
python3 ~/clawd/skills/api-provider-status/balance_checker.py all
用量统计
# 最近 12 小时用量
python3 ~/clawd/skills/api-provider-status/usage_tracker.py report 12
# 最近 24 小时用量
python3 ~/clawd/skills/api-provider-status/usage_tracker.py report 24
# JSON 格式
python3 ~/clawd/skills/api-provider-status/usage_tracker.py stats 12
首次登录设置
如果 Playwright 登录状态过期,需要重新登录:
# GitHub 登录
cd ~/clawd/skills/playwright-automation
python3 << 'EOF'
from playwright.sync_api import sync_playwright
import os
user_data_dir = os.path.expanduser('~/.playwright-data/github')
os.makedirs(user_data_dir, exist_ok=True)
with sync_playwright() as p:
context = p.chromium.launch_persistent_context(
user_data_dir, headless=False
)
page = context.pages[0] if context.pages else context.new_page()
page.goto('https://github.com/login')
input('登录完成后按 Enter...')
context.close()
EOF
# Provider-A登录
python3 << 'EOF'
from playwright.sync_api import sync_playwright
import os
user_data_dir = os.path.expanduser('~/.playwright-data/your-provider')
os.makedirs(user_data_dir, exist_ok=True)
with sync_playwright() as p:
context = p.chromium.launch_persistent_context(
user_data_dir, headless=False
)
page = context.pages[0] if context.pages else context.new_page()
page.goto('https://your-provider.example.com/')
input('登录完成后按 Enter...')
context.close()
EOF
定时任务
OpenClaw 健康监控
任务名: OpenClaw 健康监控 模型:
- 检查 OpenClaw 系统状态
- 查询 Provider-B 和 your-provider 余额
- 生成健康报告推送到 Telegram
- 异常时发送 ⚠️ 告警
文件结构
skills/api-provider-status/
├── SKILL.md # 本文档
├── balance_checker.py # 余额查询脚本
└── usage_tracker.py # 用量统计脚本(从 OpenClaw 日志读取)
登录状态存储
~/.playwright-data/
├── <provider>/model # Provider-B 登录状态
├── github/ # GitHub 登录状态
└── your-provider/ # Provider-A登录状态
OpenClaw 系统数据 (2026-02-07)
系统状态: ✅ Gateway 运行中 | 11 Agents | 7 活跃会话
通道: Telegram ✅ | WhatsApp ✅
API 供应商:
| 供应商 | 余额 | 状态 |
|---|---|---|
| Provider-B | ¥321.83 | ✅ 正常 |
| your-provider | ¥41.79 | ⚠️ 偏低 |
| GitHub Copilot | 免费额度 | ✅ 可用 |
| ZAI (智谱) | - | ✅ 可用 |
| OpenRouter VIP | - | ✅ 可用 |
Files (agi-super-team)
-
references
-
credentials.md 660 B
# 供应商登录凭据 ⚠️ 此文件包含敏感信息,请勿泄露 ## OpenRouter VIP - **Dashboard**: https://openrouter.vip/dashboard - **登录方式**: 待配置 - **余额位置**: Dashboard 首页 ## 智谱 ZAI - **Dashboard**: https://open.bigmodel.cn/console/account - **登录方式**: 待配置 - **余额位置**: 账户中心 ## Anapi - **Dashboard**: https://anapi.9w7.cn/dashboard - **登录方式**: 待配置 - **余额位置**: Dashboard 首页 --- ## 配置说明 当用户提供登录凭据时,更新此文件: ```yaml provider_name: username: xxx password: xxx # 或其他认证方式 notes: 任何特殊说明 ```
-
-
scripts
-
query_balance.py 2.7 KB
#!/usr/bin/env python3 """ API Provider Balance Query Script 查询各 API 供应商的余额(通过已知 API 端点) """ import json import sys import os from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError # 供应商配置 PROVIDERS = { "openrouter-vip": { "name": "OpenRouter VIP", "balance_url": None, # 需要浏览器登录 "dashboard": "https://openrouter.vip/dashboard" }, "zai": { "name": "智谱 ZAI", "balance_url": None, # 需要浏览器登录 "dashboard": "https://open.bigmodel.cn/console/account" }, "anapi": { "name": "Anapi", "balance_url": None, # 需要浏览器登录 "dashboard": "https://anapi.9w7.cn/dashboard" }, "openrouter": { "name": "OpenRouter", "balance_url": "https://openrouter.ai/api/v1/auth/key", "dashboard": "https://openrouter.ai/settings/keys" }, "anthropic": { "name": "Anthropic", "balance_url": None, "dashboard": "https://console.anthropic.com/settings/billing" }, "openai": { "name": "OpenAI", "balance_url": None, "dashboard": "https://platform.openai.com/usage" } } def query_openrouter_balance(api_key: str) -> dict: """Query OpenRouter balance via API""" url = "https://openrouter.ai/api/v1/auth/key" req = Request(url, headers={"Authorization": f"Bearer {api_key}"}) try: with urlopen(req, timeout=10) as resp: data = json.loads(resp.read().decode()) return { "success": True, "balance": data.get("data", {}).get("limit_remaining"), "usage": data.get("data", {}).get("usage"), "raw": data } except (URLError, HTTPError) as e: return {"success": False, "error": str(e)} def get_provider_info(provider_id: str) -> dict: """Get provider info and dashboard URL""" return PROVIDERS.get(provider_id, { "name": provider_id, "balance_url": None, "dashboard": None }) def main(): if len(sys.argv) < 2: print(json.dumps({"providers": list(PROVIDERS.keys())})) return provider = sys.argv[1] api_key = sys.argv[2] if len(sys.argv) > 2 else None info = get_provider_info(provider) result = { "provider": provider, "name": info["name"], "dashboard": info["dashboard"], "balance_api_available": info["balance_url"] is not None } if provider == "openrouter" and api_key: balance_result = query_openrouter_balance(api_key) result.update(balance_result) print(json.dumps(result, indent=2)) if __name__ == "__main__": main()
-
-
balance_checker.py 13.1 KB
#!/usr/bin/env python3 """API 供应商余额查询器 支持的供应商及查询方式: - anapi: https://anapi.9w7.cn/api/apikeys/query?key=<key> - openrouter-vip: 待添加 - your-provider: 待添加 - zai: 待添加 """ import json import os import subprocess from pathlib import Path from datetime import datetime # 配置文件路径 AUTH_PROFILES = Path.home() / ".openclaw/agents/telegram-agent/agent/auth-profiles.json" # 供应商查询配置 PROVIDERS = { "provider-b": { "name": "AIXN (Claude Opus 4.6)", "query_url": "https://ai.xn--vuq861bvij35ps8cv0uohm.com/console", "method": "playwright", "user_data_dir": "~/.playwright-data/provider-b", }, "github-copilot": { "name": "GitHub Copilot Pro", "query_url": "https://github.com/settings/copilot", "method": "playwright", "user_data_dir": "~/.playwright-data/github", }, "openrouter-vip": { "name": "OpenRouter VIP", "query_url": None, "method": "skip", # 暂不查询 }, "your-provider": { "name": "provider-a API", "query_url": "https://your-provider.example.com/console", "method": "playwright", "user_data_dir": "~/.playwright-data/your-provider", }, "zai": { "name": "ZAI (智谱)", "query_url": None, "method": "skip", # 暂不查询 }, } def load_auth_profiles(): """加载认证配置""" if AUTH_PROFILES.exists(): return json.loads(AUTH_PROFILES.read_text()) return {"profiles": {}} def query_github_copilot() -> dict: """查询 GitHub Copilot Pro 用量""" import re import time user_data_dir = os.path.expanduser('~/.playwright-data/github') if not os.path.exists(user_data_dir): return {"success": False, "provider": "github-copilot", "error": "未登录,请先运行登录流程"} try: from playwright.sync_api import sync_playwright with sync_playwright() as p: context = p.chromium.launch_persistent_context( user_data_dir, headless=True, timeout=60000 ) page = context.pages[0] if context.pages else context.new_page() page.set_default_timeout(30000) page.goto('https://github.com/settings/copilot', wait_until='domcontentloaded') time.sleep(3) text = page.inner_text('body') context.close() if not text: return {"success": False, "provider": "github-copilot", "error": "页面内容为空"} # 解析数据 # Premium requests 5.0% usage_match = re.search(r'Premium requests\s*([\d.]+)%', text) is_active = 'Copilot Pro is active' in text or 'active Copilot Pro' in text return { "success": True, "provider": "github-copilot", "status": "Active" if is_active else "Inactive", "plan": "Copilot Pro (学生认证)", "premium_usage_pct": float(usage_match.group(1)) if usage_match else 0, } except ImportError: return {"success": False, "provider": "github-copilot", "error": "Playwright 未安装"} except Exception as e: return {"success": False, "provider": "github-copilot", "error": str(e)} def query_provider_b() -> dict: """查询 AIXN 余额(需要已登录的 Playwright session)""" import re import time user_data_dir = os.path.expanduser('~/.playwright-data/provider-b') if not os.path.exists(user_data_dir): return {"success": False, "provider": "provider-b", "error": "未登录,请先运行登录流程"} try: from playwright.sync_api import sync_playwright with sync_playwright() as p: context = p.chromium.launch_persistent_context( user_data_dir, headless=True, timeout=30000 ) page = context.pages[0] if context.pages else context.new_page() page.set_default_timeout(20000) page.goto('https://ai.xn--vuq861bvij35ps8cv0uohm.com/console', wait_until='networkidle') time.sleep(2) # 等待 JS 渲染 # 获取页面文本 text = page.locator('body').text_content() context.close() if not text: return {"success": False, "provider": "provider-b", "error": "页面内容为空"} # 解析数据 - AIXN 页面格式 balance_match = re.search(r'余额[::]\s*¥?([\d.]+)', text) consumed_match = re.search(r'已消耗[::]\s*¥?([\d.]+)', text) requests_match = re.search(r'请求[次数::]\s*(\d+)', text) return { "success": True, "provider": "provider-b", "balance": float(balance_match.group(1)) if balance_match else 0, "consumed": float(consumed_match.group(1)) if consumed_match else 0, "requests": int(requests_match.group(1)) if requests_match else 0, } except ImportError: return {"success": False, "provider": "provider-b", "error": "Playwright 未安装"} except Exception as e: return {"success": False, "provider": "provider-b", "error": str(e)} def query_your-provider() -> dict: """查询星价比 API 余额(需要已登录的 Playwright session)""" import re import time user_data_dir = os.path.expanduser('~/.playwright-data/your-provider') if not os.path.exists(user_data_dir): return {"success": False, "provider": "your-provider", "error": "未登录,请先运行登录流程"} try: from playwright.sync_api import sync_playwright with sync_playwright() as p: context = p.chromium.launch_persistent_context( user_data_dir, headless=True, timeout=30000 ) page = context.pages[0] if context.pages else context.new_page() page.set_default_timeout(20000) page.goto('https://your-provider.example.com/console', wait_until='networkidle') time.sleep(2) # 等待 JS 渲染 # 获取页面文本 text = page.locator('body').text_content() context.close() if not text: return {"success": False, "provider": "your-provider", "error": "页面内容为空"} # 解析数据 # 当前余额💰43.08历史消耗💰62.56 balance_match = re.search(r'当前余额💰([\d.]+)', text) consumed_match = re.search(r'历史消耗💰([\d.]+)', text) requests_match = re.search(r'请求次数(\d+)', text) tokens_match = re.search(r'总Tokens(\d+)', text) return { "success": True, "provider": "your-provider", "balance": float(balance_match.group(1)) if balance_match else 0, "consumed": float(consumed_match.group(1)) if consumed_match else 0, "requests": int(requests_match.group(1)) if requests_match else 0, "tokens": int(tokens_match.group(1)) if tokens_match else 0, } except ImportError: return {"success": False, "provider": "your-provider", "error": "Playwright 未安装"} except Exception as e: return {"success": False, "provider": "your-provider", "error": str(e)} def query_anapi(key: str) -> dict: """查询 anapi 余额""" import urllib.request import urllib.error url = f"https://anapi.9w7.cn/api/apikeys/query?key={key}" try: req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) with urllib.request.urlopen(req, timeout=10) as resp: data = json.loads(resp.read().decode()) return { "success": True, "provider": "anapi", "status": data.get("status_display", data.get("status")), "plan": data.get("plan_display", data.get("plan_type")), "expires_at": data.get("expires_at"), "daily_limit": data.get("global_daily_limit", 0), "daily_used": data.get("daily_success_count", 0), "total_success": data.get("success_count", 0), "total_fail": data.get("fail_count", 0), "success_rate": data.get("success_rate", 0), "total_hours": data.get("total_hours"), "used_hours": data.get("used_hours"), "raw": data, } except Exception as e: return {"success": False, "provider": "anapi", "error": str(e)} def query_provider(provider: str) -> dict: """查询指定供应商的余额""" config = PROVIDERS.get(provider) if not config: return {"success": False, "provider": provider, "error": "不支持的供应商"} # GitHub Copilot 不需要 key if config["method"] == "playwright" and provider == "github-copilot": return query_github_copilot() # provider-a(用 session) if config["method"] == "playwright" and provider == "your-provider": return query_your-provider() # AIXN(用 session) if config["method"] == "playwright" and provider == "provider-b": return query_provider_b() # 跳过的供应商 if config["method"] == "skip": return {"success": False, "provider": provider, "error": "暂不查询"} return { "success": False, "provider": provider, "error": "需要手动查询", "method": config["method"], } def query_all() -> dict: """查询所有供应商""" results = {} for provider in PROVIDERS: results[provider] = query_provider(provider) return results def format_balance_report() -> str: """生成余额报告""" results = query_all() now = datetime.now().strftime('%Y-%m-%d %H:%M') lines = [ f"💰 API 余额查询 | {now}", "━━━━━━━━━━━━━━━━━━━━━━━━", "", ] for provider, data in results.items(): name = PROVIDERS[provider]["name"] if data.get("success"): status = data.get("status", "未知") plan = data.get("plan", "未知") lines.append(f"✅ **{name}**") # provider-b and provider-a特殊处理 if provider in ["provider-b", "your-provider"]: balance = data.get("balance", 0) consumed = data.get("consumed", 0) requests = data.get("requests", 0) lines.append(f" 余额: 💰{balance:.2f}") lines.append(f" 已消耗: 💰{consumed:.2f}") lines.append(f" 请求次数: {requests}") # GitHub Copilot 特殊处理 elif provider == "github-copilot": usage_pct = data.get("premium_usage_pct", 0) lines.append(f" 状态: {status} | 套餐: {plan}") lines.append(f" Premium 用量: {usage_pct}%") else: lines.append(f" 状态: {status} | 套餐: {plan}") # 每日额度 daily_limit = data.get("daily_limit", 0) daily_used = data.get("daily_used", 0) if daily_limit > 0: pct = daily_used / daily_limit * 100 lines.append(f" 今日: {daily_used}/{daily_limit} ({pct:.1f}%)") # 到期时间 expires = data.get("expires_at") if expires: exp_date = expires.split("T")[0] lines.append(f" 到期: {exp_date}") # 成功率 rate = data.get("success_rate", 0) if rate > 0: lines.append(f" 成功率: {rate*100:.1f}%") else: error = data.get("error", "未知错误") lines.append(f"⚠️ **{name}**: {error}") lines.append("") lines.append("━━━━━━━━━━━━━━━━━━━━━━━━") return "\n".join(lines) if __name__ == "__main__": import sys if len(sys.argv) < 2: print(""" API 余额查询器 用法: python balance_checker.py report # 生成报告 python balance_checker.py query <provider> # 查询指定供应商 python balance_checker.py all # JSON 格式查询所有 """) sys.exit(1) cmd = sys.argv[1] if cmd == "report": print(format_balance_report()) elif cmd == "query" and len(sys.argv) > 2: result = query_provider(sys.argv[2]) print(json.dumps(result, indent=2, ensure_ascii=False, default=str)) elif cmd == "all": print(json.dumps(query_all(), indent=2, ensure_ascii=False, default=str)) else: print(f"未知命令: {cmd}") sys.exit(1) -
SKILL.md 6 KB
--- name: api-provider-status description: 查询 API 供应商的余额、用量与可用状态,支持多供应商监控与定时汇报。 --- # API Provider Status Skill API 供应商状态查询与余额监控。 ## 功能 - 查询各 API 供应商的余额/用量 - 生成统计报告 - 支持定时自动查询 ## 支持的供应商 | 供应商 | 查询方式 | 登录状态存储 | 状态 | |--------|----------|--------------|------| | ~~Anapi (Claude)~~ | ~~API 直接查询~~ | - | ❌ 已废弃 | | Provider-B | Playwright / Browser Tool | `~/.playwright-data/provider-b` | ✅ 可用 | | GitHub Copilot | Playwright 抓取 | `~/.playwright-data/github` | ✅ 可用 | | Provider-A (your-provider) | Playwright 抓取 | `~/.playwright-data/your-provider` | ✅ 可用 | | OpenRouter VIP | API 暂不支持 | - | ⏳ | | ZAI (智谱) | 无公开 API,不查询 | - | ⏭️ 跳过 | ## 查询方法详解 ### 1. Provider-B **方式**: Browser Tool 或 Playwright 抓取 **查询页面**: `https://ai.9w7.cn/console` **登录状态**: `~/.playwright-data/<provider>/model` **抓取数据**: - 当前余额 (¥) - 历史消耗 (¥) - 请求次数 - 统计 Tokens - 统计额度 (¥) - 平均 RPM / TPM **使用 Browser Tool 查询**: 1. 打开 `https://ai.9w7.cn/console`(openclaw browser 已保存登录状态) 2. 从页面 snapshot 中提取数据看板信息 ### 2. GitHub Copilot Pro **方式**: Playwright 抓取(需要先登录) **查询页面**: `https://github.com/settings/copilot` **登录流程**: ```python from playwright.sync_api import sync_playwright import os user_data_dir = os.path.expanduser('~/.playwright-data/github') os.makedirs(user_data_dir, exist_ok=True) with sync_playwright() as p: context = p.chromium.launch_persistent_context( user_data_dir, headless=False, # 显示浏览器让用户登录 ) page = context.pages[0] if context.pages else context.new_page() page.goto('https://github.com/settings/copilot') # 用户手动登录后,登录状态会保存到 user_data_dir ``` **抓取数据**: - Premium requests 用量百分比 - 订阅状态 (Active/Inactive) **正则匹配**: ```python import re usage_match = re.search(r'Premium requests\s*([\d.]+)%', text) is_active = 'Copilot Pro is active' in text ``` ### 3. Provider-A (your-provider) **方式**: Playwright 抓取(需要先登录) **查询页面**: `https://your-provider.example.com/console` **登录流程**: ```python user_data_dir = os.path.expanduser('~/.playwright-data/your-provider') # 同上,使用 launch_persistent_context ``` **抓取数据**: - 当前余额 - 历史消耗 - 请求次数 - 总 Tokens **正则匹配**: ```python balance_match = re.search(r'当前余额💰([\d.]+)', text) consumed_match = re.search(r'历史消耗💰([\d.]+)', text) requests_match = re.search(r'请求次数(\d+)', text) tokens_match = re.search(r'总Tokens(\d+)', text) ``` ## 使用方法 ### 命令行 ```bash # 生成完整报告 python3 ~/clawd/skills/api-provider-status/balance_checker.py report # 查询指定供应商 python3 ~/clawd/skills/api-provider-status/balance_checker.py query anapi python3 ~/clawd/skills/api-provider-status/balance_checker.py query github-copilot python3 ~/clawd/skills/api-provider-status/balance_checker.py query your-provider # JSON 格式查询所有 python3 ~/clawd/skills/api-provider-status/balance_checker.py all ``` ### 用量统计 ```bash # 最近 12 小时用量 python3 ~/clawd/skills/api-provider-status/usage_tracker.py report 12 # 最近 24 小时用量 python3 ~/clawd/skills/api-provider-status/usage_tracker.py report 24 # JSON 格式 python3 ~/clawd/skills/api-provider-status/usage_tracker.py stats 12 ``` ## 首次登录设置 如果 Playwright 登录状态过期,需要重新登录: ```python # GitHub 登录 cd ~/clawd/skills/playwright-automation python3 << 'EOF' from playwright.sync_api import sync_playwright import os user_data_dir = os.path.expanduser('~/.playwright-data/github') os.makedirs(user_data_dir, exist_ok=True) with sync_playwright() as p: context = p.chromium.launch_persistent_context( user_data_dir, headless=False ) page = context.pages[0] if context.pages else context.new_page() page.goto('https://github.com/login') input('登录完成后按 Enter...') context.close() EOF # Provider-A登录 python3 << 'EOF' from playwright.sync_api import sync_playwright import os user_data_dir = os.path.expanduser('~/.playwright-data/your-provider') os.makedirs(user_data_dir, exist_ok=True) with sync_playwright() as p: context = p.chromium.launch_persistent_context( user_data_dir, headless=False ) page = context.pages[0] if context.pages else context.new_page() page.goto('https://your-provider.example.com/') input('登录完成后按 Enter...') context.close() EOF ``` ## 定时任务 ### OpenClaw 健康监控 **任务名**: OpenClaw 健康监控 **模型**: <provider>/glm-4.7 **频率**: 每天 2:00, 8:00, 14:00, 20:00 (每6小时) **功能**: - 检查 OpenClaw 系统状态 - 查询 Provider-B 和 your-provider 余额 - 生成健康报告推送到 Telegram - 异常时发送 ⚠️ 告警 ## 文件结构 ``` skills/api-provider-status/ ├── SKILL.md # 本文档 ├── balance_checker.py # 余额查询脚本 └── usage_tracker.py # 用量统计脚本(从 OpenClaw 日志读取) ``` ## 登录状态存储 ``` ~/.playwright-data/ ├── <provider>/model # Provider-B 登录状态 ├── github/ # GitHub 登录状态 └── your-provider/ # Provider-A登录状态 ``` ## OpenClaw 系统数据 (2026-02-07) **系统状态**: ✅ Gateway 运行中 | 11 Agents | 7 活跃会话 **通道**: Telegram ✅ | WhatsApp ✅ **API 供应商**: | 供应商 | 余额 | 状态 | |--------|------|------| | Provider-B | ¥321.83 | ✅ 正常 | | your-provider | ¥41.79 | ⚠️ 偏低 | | GitHub Copilot | 免费额度 | ✅ 可用 | | ZAI (智谱) | - | ✅ 可用 | | OpenRouter VIP | - | ✅ 可用 | -
usage_tracker.py 9.1 KB
#!/usr/bin/env python3 """API 用量和费用追踪器 - 从 OpenClaw 会话日志中统计实际用量""" import json import os import glob from datetime import datetime, timedelta from pathlib import Path # OpenClaw 会话日志目录 OPENCLAW_DIR = Path.home() / ".openclaw" AGENTS_DIR = OPENCLAW_DIR / "agents" # 价格配置 (每 1M tokens) PRICING = { # Anthropic "claude-opus-4-5-20250514": {"input": 15.0, "output": 75.0, "currency": "USD"}, "claude-sonnet-4-5-20250514": {"input": 3.0, "output": 15.0, "currency": "USD"}, # ZAI "glm-4.7": {"input": 0.5, "output": 0.5, "currency": "CNY"}, # OpenRouter "gpt-5.2": {"input": 2.0, "output": 8.0, "currency": "USD"}, "gpt-5.2-codex": {"input": 2.0, "output": 8.0, "currency": "USD"}, "gpt-5.1-codex-mini": {"input": 1.0, "output": 4.0, "currency": "USD"}, # Gemini "gemini-3-pro-preview": {"input": 1.25, "output": 5.0, "currency": "USD"}, "gemini-3-flash-preview": {"input": 0.075, "output": 0.3, "currency": "USD"}, "gemini-2.5-flash": {"input": 0.075, "output": 0.3, "currency": "USD"}, # GitHub Copilot (免费) "github-copilot": {"input": 0, "output": 0, "currency": "USD"}, } def get_all_session_files(): """获取所有 agent 的会话日志文件""" files = [] for agent_dir in AGENTS_DIR.iterdir(): if agent_dir.is_dir(): sessions_dir = agent_dir / "sessions" if sessions_dir.exists(): files.extend(sessions_dir.glob("*.jsonl")) return files def parse_session_file(file_path, since_timestamp=None): """解析单个会话文件,提取用量信息""" usage_records = [] try: with open(file_path, 'r', encoding='utf-8') as f: for line in f: try: entry = json.loads(line.strip()) # 只处理 message 类型 if entry.get("type") != "message": continue msg = entry.get("message", {}) # 只处理 assistant 消息(有 usage 信息) if msg.get("role") != "assistant": continue usage = msg.get("usage", {}) if not usage or usage.get("totalTokens", 0) == 0: continue # 检查时间戳 timestamp_str = entry.get("timestamp") if timestamp_str: try: timestamp = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00')) if since_timestamp and timestamp < since_timestamp: continue except: pass provider = msg.get("provider", "unknown") model = msg.get("model", "unknown") # 跳过内部 mirror if provider == "openclaw" or model == "delivery-mirror": continue usage_records.append({ "provider": provider, "model": model, "tokens_in": usage.get("input", 0), "tokens_out": usage.get("output", 0), "cache_read": usage.get("cacheRead", 0), "cache_write": usage.get("cacheWrite", 0), "timestamp": timestamp_str }) except json.JSONDecodeError: continue except Exception as e: print(f"Error reading {file_path}: {e}") return usage_records def get_usage_since(hours=12): """获取最近 N 小时的用量统计""" since = datetime.now().astimezone() - timedelta(hours=hours) all_records = [] for file_path in get_all_session_files(): # 只处理最近修改的文件 mtime = datetime.fromtimestamp(file_path.stat().st_mtime).astimezone() if mtime < since: continue records = parse_session_file(file_path, since) all_records.extend(records) # 按 provider/model 聚合 stats = {} for record in all_records: provider = record["provider"] model = record["model"] key = f"{provider}/{model}" if key not in stats: stats[key] = { "provider": provider, "model": model, "requests": 0, "tokens_in": 0, "tokens_out": 0, "cache_read": 0, "cache_write": 0, } stats[key]["requests"] += 1 stats[key]["tokens_in"] += record["tokens_in"] stats[key]["tokens_out"] += record["tokens_out"] stats[key]["cache_read"] += record["cache_read"] stats[key]["cache_write"] += record["cache_write"] # 计算费用 for key, data in stats.items(): model = data["model"] if model in PRICING: price = PRICING[model] cost = (data["tokens_in"] / 1_000_000 * price["input"] + data["tokens_out"] / 1_000_000 * price["output"]) data["cost"] = cost data["currency"] = price["currency"] else: data["cost"] = 0 data["currency"] = "USD" return { "period_hours": hours, "since": since.isoformat(), "stats": stats, "total": { "requests": sum(s["requests"] for s in stats.values()), "tokens_in": sum(s["tokens_in"] for s in stats.values()), "tokens_out": sum(s["tokens_out"] for s in stats.values()), } } def format_usage_report(hours=12) -> str: """生成用量报告""" data = get_usage_since(hours) now = datetime.now().strftime('%Y-%m-%d %H:%M') lines = [ f"📊 API 用量统计 | 最近 {hours} 小时", f"⏰ 截至: {now}", "━━━━━━━━━━━━━━━━━━━━━━━━", "", ] if not data["stats"]: lines.append("📭 暂无 API 调用记录") else: total_cost_usd = 0 total_cost_cny = 0 # 按请求数排序 sorted_stats = sorted(data["stats"].items(), key=lambda x: x[1]["requests"], reverse=True) for key, info in sorted_stats: requests = info["requests"] tokens_in = info["tokens_in"] tokens_out = info["tokens_out"] cost = info.get("cost", 0) currency = info.get("currency", "USD") # 格式化 tokens in_k = tokens_in / 1000 out_k = tokens_out / 1000 cost_str = "" if cost > 0: if currency == "USD": cost_str = f" ~${cost:.3f}" total_cost_usd += cost else: cost_str = f" ~¥{cost:.3f}" total_cost_cny += cost lines.append(f"• {key}") lines.append(f" {requests}次 | {in_k:.1f}k↓ {out_k:.1f}k↑{cost_str}") lines.extend([ "", "━━━━━━━━━━━━━━━━━━━━━━━━", f"📈 合计: {data['total']['requests']} 次请求", f"🔢 Tokens: {data['total']['tokens_in']//1000}k↓ / {data['total']['tokens_out']//1000}k↑", ]) if total_cost_usd > 0: lines.append(f"💵 USD: ~${total_cost_usd:.3f}") if total_cost_cny > 0: lines.append(f"💴 CNY: ~¥{total_cost_cny:.3f}") lines.append("━━━━━━━━━━━━━━━━━━━━━━━━") return "\n".join(lines) def get_daily_summary(date_str=None): """获取某天的用量(兼容旧接口)""" if date_str is None: return get_usage_since(24) # 对于历史日期,暂时返回空 return {"date": date_str, "stats": {}, "total": {"requests": 0, "tokens_in": 0, "tokens_out": 0}} if __name__ == "__main__": import sys if len(sys.argv) < 2: print(""" API 用量追踪器 (v2 - 从 OpenClaw 日志读取) 用法: python usage_tracker.py report [hours] # 生成报告 (默认 12 小时) python usage_tracker.py stats [hours] # JSON 格式统计 python usage_tracker.py today # 今日统计 (24h) """) sys.exit(1) cmd = sys.argv[1] if cmd == "report": hours = int(sys.argv[2]) if len(sys.argv) > 2 else 12 print(format_usage_report(hours)) elif cmd == "stats": hours = int(sys.argv[2]) if len(sys.argv) > 2 else 12 print(json.dumps(get_usage_since(hours), indent=2, ensure_ascii=False, default=str)) elif cmd == "today": print(json.dumps(get_usage_since(24), indent=2, ensure_ascii=False, default=str)) else: print(f"未知命令: {cmd}") sys.exit(1)
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.