用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/aAAaqwq/AGI-Super-Team --skill linkedin-cdp命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
币安广场合约投机雷达 v5:以最近24小时专业交易帖为主要证据,回源核验帖子, 联合币安公共合约行情、4周期K线、布林带、ATR、量能和RR,生成可审计的本地影子报告。 触发词:币安广场、扫描币安、binance square、合约机会、交易信号雷达、4小时雷达
BTC 5分钟K线实时方向预测。v5.9对抗式审查重构: 13因子收敛到3个有证据信号(half_body延续+volume放量+meanrev回归, 11个47-49%硬币因子清零) + 三层独立信息过滤(多周期4h/1h/15m趋势 + 跨资产ETH/SOL广度 + 真订单流OFI) + 移除bull×0.92惩罚/Platt置信度门控。半K线策略第2分钟执行。黑天鹅防护: ATR spike+FNG<25。Binance端点双向故障切换。
BB 双向套利策略:加密合约 10x 杠杆布林带均值回归。布林带收窄=横盘→在下轨买、上轨卖;三重过滤器(1h趋势/RSI/BB甜区)确认碗平放,轨对轨止盈(RR 2:1~4:1)。含实时WebSocket模拟盘(paper)、历史回测(simulate/backtest_daily)、币安永续实盘CLI(trade_exec)。触发:'bb套利'、'布林带'、'bollinger'、'横盘策略'、'NEAR'、'回测'、'模拟盘'、'paper trading'。
基于 SOC 职业分类
正在显示 SKILL.md
| name | linkedin-cdp |
| description | LinkedIn CDP: Input-only automation (mouse, keyboard, screenshots). Zero DOM access. |
LinkedIn automation via Chrome DevTools Protocol. Input-only (mouse/keyboard/screenshots). Zero DOM access.
LinkedInMessagesLinkedInMessagesLinkedInMessages.send_message()LinkedInSearchLinkedInProfileLinkedInConnectLinkedInConnect.screenshot_invitations()pip install websocket-client requests--remote-debugging-port=9222 (separate instance)| What | Path |
|---|---|
| Script | $HOME/linkedin-cdp/linkedin_cdp.py |
| Modules | $HOME/linkedin-cdp/linkedin_*.py |
| Rate limiter | $HOME/linkedin-cdp/rate_limiter.py |
| Screenshots | /tmp/li_screenshots/shot_*.jpg |
IMPORTANT: Use the binary path directly, NOT open -a 'Google Chrome'.
open -a on macOS opens a tab in existing Chrome instead of a separate instance.
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
'--remote-allow-origins=*' \
--user-data-dir="$HOME/chrome-debug-profile" \
"https://www.linkedin.com" > /dev/null 2>&1 &
Run in background (run_in_background: true), then verify:
sleep 3 && curl -s "http://localhost:9222/json/version"
--user-data-dir MUST differ from user's main Chrome profile$HOME/chrome-debug-profile persists login session between runsEvery session may have different window size / DPR. On first run, take a calibration screenshot and determine the coordinate mapping:
import sys, subprocess, time
sys.path.insert(0, '$HOME/linkedin-cdp')
from linkedin_cdp import LinkedInBot
bot = LinkedInBot()
bot.connect()
# Take calibration screenshot — returns file path
path = bot.take_screenshot()
print(path) # /tmp/li_screenshots/shot_0001.jpg
# Get image dimensions to determine DPR
result = subprocess.run(['sips', '-g', 'pixelWidth', '-g', 'pixelHeight', path],
capture_output=True, text=True)
print(result.stdout)
bot.close()
Then read the calibration screenshot with Read tool. Calculate:
Store the DPR for all subsequent coordinate calculations in this session.
All click_at(), _click() calls use CSS coordinates, not image pixels.
Formula: CSS_coord = image_pixel_coord / DPR
Typical Retina Mac (DPR=2, viewport ~1531x801):
import sys
sys.path.insert(0, '$HOME/linkedin-cdp')
from linkedin_cdp import LinkedInBot
bot = LinkedInBot()
bot.connect()
# All actions use CSS coordinates
bot.click_at(x, y) # click + screenshot
bot.type_text("text") # type with human-like delays
bot.scroll_wheel(delta_y=500) # scroll (keyword arg only!)
bot.take_screenshot() # returns file path (JPEG)
bot.navigate_to(url) # navigate + auto reconnect
bot.reconnect_to_tab() # reconnect WebSocket after page change
bot.close()
These modal/dialog coordinates are consistent across sessions at the same viewport size. Recalibrate if window size changes.
After clicking "Connect" on a profile:
| Element | CSS (x, y) | Notes |
|---|---|---|
| "Add a note" button | (751, 239) | White/outline button |
| "Send without a note" button | (911, 239) | Blue button |
| Note text field (click to focus) | (752, 259) | After clicking "Add a note" |
| "Send" button (with note) | (968, 393) | Blue, active after typing |
| "Cancel" button | (889, 393) |
| Element | CSS (x, y) | Notes |
|---|---|---|
| "Connect" button | (~270, 467-499) | y varies by profile layout (banner height, etc.) |
| "Message" button | (~383, 467-499) | Next to Connect |
/messaging/ — conversation list on left, active thread on right.
| Element | CSS (x, y) | Notes |
|---|---|---|
| "Write a message..." text field | (715, 604) | Click to focus, then type_text() |
| Send button | (885, 724) | Active (blue) only after typing |
/mynetwork/invitation-manager/ — lists received/sent invitations.
| Element | CSS (x, y) | Notes |
|---|---|---|
| "Accept" button (first invite) | (~920, 274) | Approximate — verify via screenshot |
| "Ignore" button (first invite) | (~838, 274) | Next to Accept |
| "Received" / "Sent" tabs | (~265, 142) / (~350, 142) | Top of page |
| Element | CSS (x, y) | Notes |
|---|---|---|
| First result name | (~305, 155) | Approximate — always verify via screenshot |
| Second result name | (~305, 280) | Approximate — positions shift with ads/banners |
LinkedInBot (linkedin_cdp.py) -- base class
|-- CDP core: connect(), _send(), close(), reconnect_to_tab()
|-- Mouse: _bezier(), _human_path(), _move_to(), _click(), _maybe_fake_hover()
|-- Keyboard: type_text(), press_key()
|-- Scroll: scroll_wheel()
|-- Screenshot: take_screenshot() -> path, take_screenshot_base64(), save_screenshot()
|-- Navigation: navigate_to(), wait_for_page()
|-- Convenience: click_at(x,y) -> screenshot, scroll_and_screenshot()
|
+-- LinkedInMessages (linkedin_messages.py)
+-- LinkedInSearch (linkedin_search.py)
+-- LinkedInProfile (linkedin_profile.py)
+-- LinkedInConnect (linkedin_connect.py)
| File | Class | Key Methods |
|---|---|---|
linkedin_cdp.py | LinkedInBot | click_at(), type_text(), scroll_wheel(), take_screenshot() -> path, take_screenshot_base64(), navigate_to() |
linkedin_messages.py | LinkedInMessages | screenshot_conversations(), open_conversation(), send_message(), collect_screenshots() |
linkedin_search.py | LinkedInSearch | search_people(), search_companies(), next_page() |
linkedin_profile.py | LinkedInProfile | view_profile(), screenshot_full_profile(), scroll_to_section() |
linkedin_connect.py | LinkedInConnect | view_profile(), send_connection_note(), screenshot_invitations(), accept_invitation() |
rate_limiter.py | RateLimiter | can_search(), can_view_profile(), wait_if_needed() |
import sys, time
sys.path.insert(0, '$HOME/linkedin-cdp')
from linkedin_cdp import LinkedInBot
bot = LinkedInBot()
bot.connect()
# 1. Search
bot.navigate_to('https://www.linkedin.com/search/results/people/?keywords=Name%20Company')
time.sleep(4)
bot.reconnect_to_tab()
time.sleep(2)
# take_screenshot() returns file path — read with Read tool to find coordinates
# 2. Click profile (coordinates from screenshot / DPR)
path = bot.click_at(305, 155) # first result — verify from screenshot!
time.sleep(4)
bot.reconnect_to_tab()
# Read path with Read tool to verify correct profile
# 3. Click Connect (~270, 467-499 — verify from screenshot)
bot.click_at(270, 499)
time.sleep(2)
# Modal appears
# 4. Click "Add a note" (fixed coordinate)
bot.click_at(751, 239)
time.sleep(1.5)
# 5. Click text field + type note
bot._click(752, 259)
time.sleep(0.5)
bot.type_text("Hi Name, personalized note here...")
time.sleep(1)
# 6. Click Send (fixed coordinate)
bot.click_at(968, 393)
# Done — verify "Pending" status + "Invitation sent" toast
bot.close()
from linkedin_messages import LinkedInMessages
lm = LinkedInMessages()
lm.connect()
path = lm.screenshot_conversations()
# path = '/tmp/li_screenshots/shot_NNNN.jpg' — read with Read tool
path = lm.open_conversation(200, 350) # coordinates from screenshot
# Read path with Read tool
lm.close()
import time
from linkedin_profile import LinkedInProfile
prof = LinkedInProfile()
prof.connect()
prof.navigate_to('https://linkedin.com/in/username')
time.sleep(4)
prof.reconnect_to_tab()
paths = [prof.take_screenshot()]
for _ in range(4):
prof.scroll_wheel(delta_y=500)
time.sleep(1.5)
paths.append(prof.take_screenshot())
# paths = ['/tmp/li_screenshots/shot_0001.jpg', ...] — read each with Read tool
prof.close()
| Problem | Solution |
|---|---|
WebSocketConnectionClosedException after navigate | Call reconnect_to_tab() — page change breaks WebSocket |
| Click misses target | Verify DPR: image_pixels / DPR = CSS coords. Re-run calibration. |
screenshot_full_profile() crashes | Use manual navigate + reconnect + scroll loop (see View Profile example) |
scroll_wheel() TypeError | Use keyword arg only: scroll_wheel(delta_y=500), NOT positional args |
| Chrome opens tab in existing browser | Use binary path directly, NOT open -a 'Google Chrome' |
| CDP port conflict | Change --remote-debugging-port=9223 and update CDP_PORT in linkedin_cdp.py |
| Modal coordinates wrong | Window resized? Re-run calibration. Fixed coords assume viewport ~1531x801. |
scroll_wheel() takes delta_y keyword arg only. Not positional.navigate_to() or page-changing click, always reconnect_to_tab().sys.path.insert(0, '$HOME/linkedin-cdp') before imports./tmp/li_screenshots/shot_*.jpg — read them with Read tool. Old files auto-cleaned (keeps last 50).| Action | Conservative | Moderate |
|---|---|---|
| Profile views | 50 | 100 |
| Searches | 20 | 50 |
| Connection requests | 15 | 25 |
| Messages sent | 30 | 50 |
Zero DOM access — NEVER Runtime.evaluate, querySelector, innerText.
Screenshot-based reading — Page.captureScreenshot (JPEG, quality 80) saved to files, not base64 in memory.
Human-like mouse — unique Bezier curves, tremor, overshoot, micro-pauses, speed variation.
Real Chrome — not headless. Normal fingerprint.
Rate limiting — built-in daily caps.
Human-in-the-loop — Claude reads screenshots, adds natural irregularity.
Runtime.evaluate or CDP Runtime domain — #1 way bots get caughtAfter sending a connection request or message, ALWAYS log to CRM:
companies.csv (if new)people.csv (if new)leads.csv (stage=new, source=linkedin, source_direction=outbound)activities.csv with the exact message text in notes field:
type: outreachchannel: linkedindirection: outboundsubject: "LinkedIn connection request with note" (or "LinkedIn message")notes: the full text of the message sentDo NOT skip step 4. The message text must be preserved in CRM for follow-up context.
add-lead — add found contacts to CRMupdate-lead — update lead status after outreachlog-activity — log activity to activities.csvemail-send-bulk — follow up via email after LinkedIn connect