Your LinkedIn surface. Slowly.
A local Playwright tool that drives your own logged-in Chromium. Two surfaces: clean up old comments; post curated cybersecurity news in Danish. No tokens, no third-party server, no password automation.
What it does
It opens your browser. It walks to your activity-comments page. It clicks the menu on each comment. It picks Delete. It confirms. It waits five to eight seconds. It does that again. The point is that it should not look like a script.
What it does not do
By design
How it works
Sign in once
npm run login opens Chromium against a local profile directory. You sign in. The script saves the session and closes itself.
Dry-run first
npm run run:cleaner -- --dry-run enumerates what it would delete and writes nothing. Read the output. Decide what to skip.
Run, slowly
Default pacing is one delete every five to eight seconds, capped at 200 per hour and 500 per day. State persists in state/processed.json so you can stop and resume.
Install
Node 20 or newer. Then:
# clone & set up git clone https://github.com/cocodedk/in-optimizer.git cd in-optimizer npm install npx playwright install chromium # sign in once (manual, in the opened window) npm run login # enumerate what would be deleted npm run run:cleaner -- --dry-run # delete, slowly npm run run:cleaner
Sister surface: cyber-news (Danish)
Same persistent profile, opposite direction. Fetch a public cybersecurity tweet, classify severity (info, notable, critical, zero-day), translate to simple Danish, run /humanizer-da twice, post to your feed with the original images. One post per invocation, with a confirmation gate before the destructive click. Live-verified on 2026-04-30.
# discover new IDs from a public handle npm run cyber-news -- discover --handle=IntCyberDigest # fetch one tweet (text + media + classification) npm run cyber-news -- fetch --id=<TWEETID> \ --media-out=state/cybernews/media/<TWEETID> # post (after writing the Danish draft) npm run cyber-news -- post --id=<TWEETID> \ --draft=state/cybernews/drafts/<TWEETID>.md \ --media-dir=state/cybernews/media/<TWEETID> \ --severity=zero-day
Architecture
Each module sits under 200 lines. Selectors live in one file per surface; when LinkedIn renames a class, you update one place.
src/ pace.ts ← jittered delays, seeded RNG scheduler.ts ← hourly & daily caps state.ts ← jsonl log + processed-set + atomic flush selectors.ts ← comment-cleaner DOM selectors commentDetector.ts ← enumerate own comments delete.ts ← click menu → confirm scroll.ts ← infinite-scroll + show-more runner.ts ← cleaner orchestration cybernews/ fetch.ts ← X syndication API + media severity.ts ← zero-day | critical | notable | info hashtags.ts ← #cybersikkerhed + topical tags selectors-li.ts ← composer selectors (en + da) poster.ts ← Playwright compose + attach + submit