Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
184 lines
7.7 KiB
Python
184 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Fast parallel LLM API key verifier — Python version.
|
|
|
|
Uses urllib instead of spawning curl subprocesses per key.
|
|
"""
|
|
|
|
import time
|
|
import json
|
|
from pathlib import Path
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
from collections import defaultdict
|
|
import urllib.request
|
|
import urllib.error
|
|
|
|
RESULTS_DIR = Path(__file__).parent / "results"
|
|
KEYS_FILE = RESULTS_DIR / "extracted_keys.txt"
|
|
LIVE_FILE = RESULTS_DIR / "live_keys_raw.txt"
|
|
|
|
# Provider verify configs
|
|
VERIFY_CONFIGS = {
|
|
"OpenAI": {"url": "https://api.openai.com/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"Anthropic": {"url": "https://api.anthropic.com/v1/models", "headers": {"x-api-key": "{key}", "anthropic-version": "2023-06-01"}},
|
|
"Google": {"url": "https://generativelanguage.googleapis.com/v1/models?key={key}", "headers": {}},
|
|
"HuggingFace": {"url": "https://huggingface.co/api/whoami-v2", "headers": {"Authorization": "Bearer {key}"}},
|
|
"Groq": {"url": "https://api.groq.com/openai/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"Replicate": {"url": "https://api.replicate.com/v1/account", "headers": {"Authorization": "Token {key}"}},
|
|
"TogetherAI": {"url": "https://api.together.xyz/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"DeepSeek": {"url": "https://api.deepseek.com/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"OpenRouter": {"url": "https://openrouter.ai/api/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"Perplexity": {"url": "https://api.perplexity.ai/chat/completions", "headers": {"Authorization": "Bearer {key}"}},
|
|
"VolcanoArk": {"url": "https://ark.cn-beijing.volces.com/api/v3/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"ZhipuAI": {"url": "https://open.bigmodel.cn/api/paas/v4/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"DashScope": {"url": "https://dashscope.aliyuncs.com/compatible-mode/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"Moonshot": {"url": "https://api.moonshot.cn/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"MiniMax": {"url": "https://api.minimaxi.com/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"LingyiWanwu": {"url": "https://api.lingyiwanwu.com/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"StepFun": {"url": "https://api.stepfun.com/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"SiliconFlow": {"url": "https://api.siliconflow.cn/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"LongCat": {"url": "https://api.longcat.chat/openai/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"OllamaCloud": {"url": "https://api.ollama.com/api/tags", "headers": {"Authorization": "Bearer {key}"}},
|
|
"FreeModel": {"url": "https://api.freemodel.dev/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"XKiro": {"url": "https://api.xkiro.com/v1/models", "headers": {"Authorization": "Bearer {key}"}},
|
|
"Exa": {"url": "https://api.exa.ai/search", "method": "POST",
|
|
"headers": {"x-api-key": "{key}", "Content-Type": "application/json"},
|
|
"body": {"query": "auto", "numResults": 1, "type": "auto"}},
|
|
}
|
|
|
|
# Providers to skip (too many false positives, or no simple verify endpoint)
|
|
SKIP_PROVIDERS = {
|
|
"Mistral", "Cohere", "AzureOpenAI",
|
|
"iFlytekSpark", "iFlytekCodingPlan",
|
|
"TencentHunyuan",
|
|
"BaiduQianfan",
|
|
"CSDNCodingPlan", "HuaweiCodeArts", "MiMo", "InfiniAI", "JDCloud",
|
|
"MooreThreads", "Kuaishou", "UCloud", "Anomaly", "ChinaUnicom",
|
|
"SCNet", "AlibabaCodingPlan", "Zyloo",
|
|
}
|
|
|
|
|
|
def verify_key(provider, key):
|
|
"""Verify a key against its provider API. Returns True if live, False if dead, None if skipped."""
|
|
if provider in SKIP_PROVIDERS:
|
|
return None
|
|
if provider not in VERIFY_CONFIGS:
|
|
return None
|
|
|
|
cfg = VERIFY_CONFIGS[provider]
|
|
url = cfg["url"].replace("{key}", key)
|
|
headers = {hname: hval.replace("{key}", key) for hname, hval in cfg["headers"].items()}
|
|
|
|
method = cfg.get("method", "GET")
|
|
data = None
|
|
if "body" in cfg:
|
|
data = json.dumps(cfg["body"]).encode()
|
|
req = urllib.request.Request(url, headers=headers, method=method, data=data)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
code = resp.getcode()
|
|
return code == 200 or (provider == "Perplexity" and code not in (401, 0))
|
|
except urllib.error.HTTPError as e:
|
|
code = e.code
|
|
if provider == "Perplexity":
|
|
return code not in (401, 0)
|
|
return code == 200
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def main():
|
|
# Read keys
|
|
keys = []
|
|
with open(KEYS_FILE) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
parts = line.split("|", 3)
|
|
if len(parts) >= 2:
|
|
provider = parts[0]
|
|
key = parts[1]
|
|
url = parts[2] if len(parts) > 2 else ""
|
|
desc = parts[3] if len(parts) > 3 else ""
|
|
keys.append((provider, key, url, desc))
|
|
|
|
print(f"Total keys: {len(keys)}")
|
|
|
|
# Group by provider and deduplicate
|
|
by_provider = defaultdict(dict) # provider -> {key: (url, desc)}
|
|
for provider, key, url, desc in keys:
|
|
if provider in SKIP_PROVIDERS:
|
|
continue
|
|
if provider not in VERIFY_CONFIGS:
|
|
continue
|
|
if key not in by_provider[provider]:
|
|
by_provider[provider][key] = (url, desc)
|
|
|
|
# Print summary
|
|
print("\nKeys by provider (deduplicated):")
|
|
total_to_verify = 0
|
|
for provider in sorted(by_provider.keys()):
|
|
n = len(by_provider[provider])
|
|
total_to_verify += n
|
|
print(f" {provider:20s}: {n:6d}")
|
|
print(f" {'TOTAL':20s}: {total_to_verify:6d}")
|
|
|
|
# Build verify list
|
|
to_verify = []
|
|
for provider, keydict in by_provider.items():
|
|
for key, (url, desc) in keydict.items():
|
|
to_verify.append((provider, key, url, desc))
|
|
|
|
# Verify in parallel
|
|
live_keys = []
|
|
dead_count = 0
|
|
processed = 0
|
|
start_time = time.time()
|
|
|
|
print(f"\nVerifying {len(to_verify)} keys with 30 threads...")
|
|
|
|
with ThreadPoolExecutor(max_workers=30) as pool:
|
|
futures = {pool.submit(verify_key, p, k): (p, k, u, d) for p, k, u, d in to_verify}
|
|
|
|
for future in as_completed(futures):
|
|
provider, key, url, desc = futures[future]
|
|
processed += 1
|
|
|
|
try:
|
|
result = future.result()
|
|
except Exception:
|
|
result = False
|
|
|
|
if result is True:
|
|
live_keys.append((provider, key, url, desc))
|
|
elif result is False:
|
|
dead_count += 1
|
|
|
|
if processed % 1000 == 0:
|
|
elapsed = time.time() - start_time
|
|
rate = processed / elapsed
|
|
print(f" [{processed}/{len(to_verify)}] live={len(live_keys)} dead={dead_count} ({rate:.0f}/s)")
|
|
|
|
elapsed = time.time() - start_time
|
|
print(f"\nDone in {elapsed:.1f}s: {len(live_keys)} live, {dead_count} dead")
|
|
|
|
# Write results
|
|
with open(LIVE_FILE, "w") as f:
|
|
for provider, key, url, desc in sorted(live_keys):
|
|
f.write(f"LIVE|{provider}|{key}|{url}|{desc}\n")
|
|
|
|
print(f"Live keys written to: {LIVE_FILE}")
|
|
|
|
# Print live keys by provider
|
|
live_by_provider = defaultdict(int)
|
|
for provider, key, url, desc in live_keys:
|
|
live_by_provider[provider] += 1
|
|
|
|
print("\nLive keys by provider:")
|
|
for provider in sorted(live_by_provider.keys()):
|
|
print(f" {provider:20s}: {live_by_provider[provider]}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|