Automate your LinkedIn presence: post content, track engagement, generate ideas, and grow your audience.
CRITICAL: All scripts that use openclaw browser commands MUST clean up tabs after execution to prevent accumulation.
This pattern preserves pre-existing tabs and only closes tabs opened during script execution:
bash
# Global variable to store baseline tabs
BASELINE_TABS=""
# Capture baseline BEFORE any browser actions
capture_baseline_tabs() {
BASELINE_TABS=$(openclaw browser tabs 2>/dev/null | grep -oE '\[t[0-9]+' | sed 's/\[//g' | sort -u || echo "")
}
# Cleanup function - closes ONLY tabs opened during script execution
cleanup_browser_tabs() {
CURRENT_TABS=$(openclaw browser tabs 2>/dev/null | grep -oE '\[t[0-9]+' | sed 's/\[//g' | sort -u || echo "")
# Find NEW tabs (not in baseline)
NEW_TABS=$(comm -13 <(echo "$BASELINE_TABS") <(echo "$CURRENT_TABS") 2>/dev/null || echo "")
# Close only new tabs
echo "$NEW_TABS" | while read TAB_ID; do
[[ -n "$TAB_ID" ]] && openclaw browser close "$TAB_ID" 2>/dev/null || true
done
}
# Capture baseline immediately (before first browser command)
capture_baseline_tabs
# Set EXIT trap for cleanup (runs on success or failure)
trap cleanup_browser_tabs EXIT
Benefits of this approach:
bash
# 1. Label tabs when opening
openclaw browser open --label "my-tab-label" https://...
# 2. Set EXIT trap for cleanup
cleanup_browser_tabs() {
TABS=$(openclaw browser tabs 2>/dev/null || echo "")
TAB_ID=$(echo "$TABS" | grep "my-tab-label" | head -1 | sed -n 's/.*\[\(t[0-9]*\).*/\1/p')
[[ -n "$TAB_ID" ]] && openclaw browser close "$TAB_ID"
}
trap cleanup_browser_tabs EXIT
Use label-based pattern when:
bash
# After completing browser workflow:
# Find tab by label and close with short ID (e.g., t74)
openclaw browser tabs | grep "my-tab-label" | sed -n 's/.*\[\(t[0-9]*\).*/\1/p' | xargs -I {} openclaw browser close {}
bash
openclaw browser tabs # Check tab count before/after runs
See scripts/analytics-extract.sh for a complete example with:
bash
# Post content (feed post, up to 3,000 chars)
{baseDir}/scripts/post.sh "Your post content here"
# Post with image
{baseDir}/scripts/post.sh "Content" --image /path/to/image.png
# Publish native article (long-form, up to 120,000 words, Google-indexed)
{baseDir}/scripts/article.sh --title "Title" --subtitle "Subtitle" --content "Content" [--cover-image /path/to/image]
# Get engagement stats for recent posts
{baseDir}/scripts/analytics.sh
# Generate content ideas based on trending topics
{baseDir}/scripts/ideas.sh [topic]
# Engage with feed (like/comment on relevant posts)
{baseDir}/scripts/engage.sh --limit 10
Use browser automation to post:
For scheduled posts, use OpenClaw cron:
text
cron add --schedule "0 9 * * 1-5" --payload "Post my LinkedIn content: [content]"
Use browser automation to publish articles (up to 120,000 words, searchable on Google):
Article vs Post:
FeaturePostArticleLength1,200-3,000 chars120,000 wordsGoogle-indexed❌ No✅ YesLifespan24-72 hoursPermanentEditorSimpleRich (H1-H6, images, embeds)LocationFeedActivity section
See references/content-strategy.md for:
See references/engagement.md for:
The analytics script extracts:
Key LinkedIn selectors (as of 2026):
text
# Posts
Post button: button[aria-label="Start a post"]
Post editor: div.ql-editor[data-placeholder]
Submit post: button.share-actions__primary-action
# Articles
Write article button: button containing "Write article" (top of feed)
Article title: input[placeholder*="title"] or h1[contenteditable]
Article body: div[contenteditable="true"] or div.editor-content
Publish button: button containing "Publish"
# General
Like button: button[aria-label*="Like"]
Comment button: button[aria-label*="Comment"]
Profile stats: section.pv-top-card-v2-ctas
LinkedIn enforces activity limits. Stay under:
Login required: Ensure browser profile has active LinkedIn session
Rate limited: Reduce activity, wait 24h
Selector not found: LinkedIn may have updated UI, check selectors
Article editor not loading: Try desktop browser (mobile has limited editor support)
Tabs accumulating: Scripts use selective cleanup (baseline comparison). If tabs remain: bash
# Verify cleanup trap exists in script
grep "trap cleanup" scripts/analytics-extract.sh
# Check what the script considers "new tabs" (debug)
# Run this DURING script execution to see baseline vs current
# Manually close LinkedIn-related tabs if needed (use short tab IDs like t74)
openclaw browser tabs | grep linkedin.com | sed -n 's/.*\[\(t[0-9]*\).*/\1/p' | \
while read tab; do openclaw browser close "$tab"; done
Zombie tabs after script run: Script may have crashed before cleanup, OR tabs were pre-existing (preserved by design). Verify: bash
# Check if tabs were pre-existing (not script-created)
openclaw browser tabs
# If truly zombie tabs, close by label pattern
openclaw browser tabs | grep "linkedin-analytics-tab" | sed -n 's/.*\[\(t[0-9]*\).*/\1/p' | xargs -I {} openclaw browser close {}
openclaw browser tabs | grep "linkedin-activity-tab" | sed -n 's/.*\[\(t[0-9]*\).*/\1/p' | xargs -I {} openclaw browser close {}
# Nuclear option: Close ALL tabs (use with caution - closes pre-existing too)
openclaw browser tabs | grep -oE '\[t[0-9]+' | sed 's/\[//g' | while read tab; do openclaw browser close "$tab"; done
Browser unresponsive: Restart browser (nuclear option): bash
openclaw browser stop
openclaw browser start