High-reliability, multi-agent-safe LinkedIn automation engine and CLI built on Chrome DevTools Protocol (CDP).
Disclaimer & Responsible Use: linkedin-ops is an independent open-source tool built for developer automation research, testing, and workflow automation via local Chrome DevTools Protocol (CDP). This project is not affiliated with, endorsed by, or sponsored by LinkedIn Corporation. Automated interaction with LinkedIn must comply with applicable platform terms and policies. Always maintain realistic human delays and respect LinkedIn rate limits to safeguard account health.
npm install
node index.mjs setup
The setup wizard automatically:
- Locates your Google Chrome binary (macOS, Linux, Windows).
- Discovers your existing Chrome profiles from system User Data so you can pick your logged-in account (or create a dedicated project session).
- Saves settings to
.linkedin-opsrc(no manual path pasting required). - Generates a one-click launcher (
start-chrome.command/start-chrome.bat).
npm run doctor
Checks CDP port 9222 reachability, browser version, user agent, and verifies an authenticated LinkedIn session.
node index.mjs connect --profile="https://www.linkedin.com/in/username" --note="Hi, loved your post on distributed systems..."
(Dry-run mode navigates and fills the note without submitting. Add --send only when ready to live submit).
- Automated Runtime Fallback : Daily commands ping port 9222. If Chrome is closed,
linkedin-opsautomatically launches it using your configured profile. If Chrome is already running without remote debugging enabled, it detects the profile lock and prompts to restart cleanly. - Cross-Process Mutex Locking : Serializes execution across concurrent agents and processes (
linkedin.lock). Prevents session collisions, focus-stealing, and rate-limiting. - Isolated Ephemeral Tabs : Each operation spawns its own dedicated Chrome tab via
PUT /json/newand connects directly via target ID. - Auto-Cleanup : Guarantees tab destruction (
/json/close/<targetId>) in afinallyblock on success or failure, eliminating zombie tab leaks.
node index.mjs setup
node index.mjs doctor
node index.mjs connect --profile="https://www.linkedin.com/in/username" --note="Hi, loved your post..."
node index.mjs connect --profile="https://www.linkedin.com/in/username" --note="Hi, loved your post..." --send
node index.mjs dm --profile="https://www.linkedin.com/in/username" --message="Checking in on our conversation." --send
node index.mjs comment --url="https://www.linkedin.com/posts/..." --text="Great insights on distributed systems!" --send
node index.mjs search --company="Google" --keywords="recruiter" --limit=10 --json
node index.mjs lock
node index.mjs lock release
js
import { withLock, withDedicatedTab, sendConnectionInvite } from 'linkedin-ops';
await withLock('outreach-task', async () => {
await withDedicatedTab({ port: 9222 }, async (cdp) => {
const result = await sendConnectionInvite(cdp, {
linkedinUrl: 'https://www.linkedin.com/in/username',
note: 'Hi, loved your work on distributed systems.',
send: true
});
console.log(result);
});
});
For multi-profile outreach and candidate sweeps, see campaigns/batch-connect.example.mjs:
cp campaigns/batch-connect.example.mjs campaigns/my-outreach.mjs
node campaigns/my-outreach.mjs
(User campaigns and harvested datasets in data/ and campaigns/ are gitignored and never committed to source control).
- DATA_CONTRACT.md : Strict definition of the System Layer vs User Layer boundaries.
- Architecture Blueprint : Deep dive into CDP socket lifecycle, locking mechanisms, and tab management.