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

  • No API tokens. Cookies stay in your own browser profile directory.
  • No third-party server. The code runs on your machine, against your account.
  • No password automation. You sign in by hand. Once.
  • No CAPTCHA solver, no headless-stealth shim, no proxy rotator. Out of scope.

How it works

I.

Sign in once

npm run login opens Chromium against a local profile directory. You sign in. The script saves the session and closes itself.

II.

Dry-run first

npm run run:cleaner -- --dry-run enumerates what it would delete and writes nothing. Read the output. Decide what to skip.

III.

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

Author

Babak Bandpey cocode.dk · LinkedIn · GitHub