npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cyborg-hunter

v0.5.0

Published

Detect LLM-assisted 'cyborg' behavior in online behavioral experiments

Readme

cyborg-hunter

Detects AI-tool use during browser-based behavioral experiments. Captures paste, copy, drag, tab-away, mouse trajectories, browser sidebar openings, and other signals that compromise data quality on Prolific / MTurk / classroom studies. Produces a triage report ranking participants by suspiciousness.

Optional companion deterrence modules (extension-guard-friction.js, extension-guard-honeypot.js) ship in the same package: friction enforces fullscreen + blocks sidebars + scrambles content during violations + asks cooperative LLMs to refuse; honeypot exposes both hidden and visible bait fields that AI agents fill while human participants don't see them.

Example: 100-participant Prolific pilot

| Rank | Participant | Score | Hard | Reason | |------|-------------|-------|------|--------| | 1 | 67c1d6d98c8787be36609212 | 4078 | no | 1 tab-away 3–10s; 2 flickers <3s; fast typing on 9 trials; 1015 synthetic insertions | | 2 | 69bc21dd22f17b2337957511 | 179 | YES | 21 paste events; 3 copy events; 9 tab-aways ≥10s; 3 sidebar events; 21 layout shifts | | 3 | 69b9f3184e0896ef41b99aa8 | 152 | no | 38 synthetic insertions | | 4 | 5d350282cec7150015d16494 | 131 | YES | 5 paste events; 10 copy events; 22 tab-aways ≥10s; 5 tab-aways 3–10s; 15 flickers <3s |

6 hard-flagged + 12 soft-flagged out of 100. Per-participant tab-away timeline (rank 4, 42 events across 15 trials):

Tab-away timeline showing 42 tab-away events across 15 trials. Most trials have multiple long red bars (≥10s tab-aways), with some shorter orange (3-10s) and grey flicker (<3s) events.

Repo layout

  • src/core/ — signal-collection library (the monitor)
  • src/jspsych/ — jsPsych extension adapters (one per concern)
  • src/cli/ + bin/ — CLI that turns saved data into the triage report
  • tests/, docs/, studies/ — tests, package docs, full-study fixtures

Install

CLI (analysis):

npm install -g cyborg-hunter

Browser (experiment page) — load via unpkg or copy dist/*.js into your project:

<!-- Signal collection -->
<script src="https://unpkg.com/cyborg-hunter/dist/cyborg-hunter.min.js"></script>
<script src="https://unpkg.com/cyborg-hunter/dist/extension-cyborg-hunter.js"></script>
<!-- Optional: deterrence + bait detection -->
<script src="https://unpkg.com/cyborg-hunter/dist/extension-guard-friction.js"></script>
<script src="https://unpkg.com/cyborg-hunter/dist/extension-guard-honeypot.js"></script>

For production studies, pin a version: https://unpkg.com/[email protected]/dist/....

Plug into a jsPsych experiment

const jsPsych = initJsPsych({
  extensions: [
    { type: jsPsychCyborgHunter, params: { participantId: participant_id, preset: 'standard' } },
    { type: jsPsychGuardFriction },     // optional
    { type: jsPsychGuardHoneypot }      // optional
  ],
  on_finish: function () {
    jsPsych.extensions['guard-friction'].finalize();   // stop friction first
    jsPsych.extensions['guard-honeypot'].finalize();   // then attach data
    jsPsych.extensions['cyborg-hunter'].finalize();    // then save
    jsPsych.data.get().localSave('csv', 'data.csv');
  }
});

timeline.push(jsPsychGuardFriction.entryTrial());     // user-gesture fullscreen entry
timeline.forEach(t => t.extensions = (t.extensions || []).concat([
  { type: jsPsychCyborgHunter },
  { type: jsPsychGuardFriction },
  { type: jsPsychGuardHoneypot }
]));
jsPsych.run(timeline);

participantId must exist before initJsPsych. Full walk-through (per-trial params, standalone non-jsPsych use, opt-in/exclude modes): docs/using-cyborg-hunter.md.

Generate a report

cd <your-data-dir>
cyborg-hunter init                # writes cyborg-hunter.config.json
# edit config: dataDir, filePattern, participantIdField
cyborg-hunter report              # writes ./cyborg-hunter-report/
open cyborg-hunter-report/index.html

Output: summary.csv (per-participant columns), triage.md (ranked list), event-log.csv (chronological events), images/ (per-participant mouse paths, tab timelines, typing profiles), index.html (landing page).

What it detects

| Signal | How | Class | |---|---|---| | Paste | Clipboard paste events | Hard (count threshold) | | Drag-and-drop | drop events on inputs | Hard (count threshold) | | Copy | Clipboard copy events | Soft (weighted) | | Tab-away | visibilitychange + blur/focus | Soft (weighted) | | Browser sidebar | innerWidth delta + layout compression | Soft (weighted) | | Suspicious typing speed | chars/sec exceeding preset threshold | Soft (weighted) | | Synthetic insertion | text appearing without keystrokes | Soft (weighted) | | Foreign input | typing landing outside experiment container | Soft (weighted) | | Idle gaps | input inactivity | Soft (weighted) | | AI-extension content scripts | DOM scan for known extension selectors | Soft (weighted) | | Mouse trajectories | 20Hz polling + path-efficiency metrics | Diagnostic | | Window/screen geometry | polled + resize-event capture, with zoom inference | Diagnostic |

Three presets: permissive / standard (default) / strict. Per-signal thresholds: docs/signals-reference.md.

The optional guard extensions add: fullscreen / sidebar / focus enforcement (with content-scrambling overlay on violation), AI refusal notices in the DOM, and honeypot fields (hidden + visible bait) that catch sidebar-LLMs and agentic browsers (Browser Use, Operator, Computer Use) — captured as ai_use / ai_report columns.

What it doesn't detect

  • Native browser AI sidebars (Chrome Gemini, Edge Copilot) — they leave no extension content scripts. The innerWidth_delta heuristic still catches them as generic sidebar events, but the named-extension column stays empty.
  • Screen-share / second device — an LLM on a phone reading the screen leaves no in-browser trace.
  • AI text edited and retyped — a determined participant who retypes character-by-character without tab-switching looks clean. Mouse-trajectory and typing-rhythm signals raise the bar but it isn't a polygraph.

Documentation

Citation

@software{konuk2026cyborghunter,
  author  = {Konuk, Can},
  title   = {cyborg-hunter: integrity monitoring for online behavioral experiments},
  year    = {2026},
  url     = {https://github.com/konukcan/cyborg-hunter},
  version = {0.4.0}
}

A "Cite this repository" button is rendered from CITATION.cff.

License

MIT