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

@callowayisweird/source-rcon

v0.1.0

Published

Production-grade Source Engine RCON client for Node.js/Bun. Zero runtime dependencies.

Readme

@callowayisweird/source-rcon

Production-grade Source Engine RCON client for Node.js and Bun. Zero runtime dependencies, full TypeScript support.

Install

npm install @callowayisweird/source-rcon

Usage

Basic Connection

import { Rcon } from '@callowayisweird/source-rcon'

const rcon = new Rcon({
  host: '127.0.0.1',
  port: 27015,
  password: 'your_rcon_password',
})

await rcon.connect()

const status = await rcon.execute('status')
console.log(status)

await rcon.disconnect()

Events

const rcon = new Rcon({
  host: '127.0.0.1',
  port: 27015,
  password: 'secret',
})

rcon.on('connect', () => console.log('Connected'))
rcon.on('disconnect', () => console.log('Disconnected'))
rcon.on('error', (err) => console.error('Error:', err))

await rcon.connect()

Auto-Reconnection

const rcon = new Rcon({
  host: '127.0.0.1',
  port: 27015,
  password: 'secret',
  autoReconnect: true,
  maxReconnectAttempts: 5,
  reconnectDelay: 3000,
})

rcon.on('reconnecting', (attempt) => {
  console.log(`Reconnecting... attempt ${attempt}`)
})

await rcon.connect()

Multi-Server Pool

import { RconPool } from '@callowayisweird/source-rcon'

const pool = new RconPool([
  { host: '10.0.0.1', port: 27015, password: 'pass1' },
  { host: '10.0.0.2', port: 27015, password: 'pass2' },
  { host: '10.0.0.3', port: 27015, password: 'pass3' },
])

await pool.connectAll()

// Execute on a specific server
const status = await pool.execute('10.0.0.1:27015', 'status')

// Execute on all servers
const results = await pool.executeAll('status')
for (const [server, response] of results) {
  console.log(`${server}: ${response}`)
}

await pool.disconnectAll()

Permission-Gated RCON

import { Rcon, GatedRcon } from '@callowayisweird/source-rcon'

const rcon = new Rcon({
  host: '127.0.0.1',
  port: 27015,
  password: 'secret',
})

await rcon.connect()

const gated = new GatedRcon(rcon, {
  allowedUsers: ['STEAM_0:1:12345678', 'STEAM_0:1:87654321'],
  permissions: {
    'STEAM_0:1:12345678': ['say *', 'status', 'changelevel *'], // admin
    'STEAM_0:1:87654321': ['say *', 'status'],                   // moderator
  },
  rateLimit: {
    windowMs: 60000,  // 1 minute
    maxCommands: 10,   // max 10 commands per minute
  },
  onExecute: (steamId, command, response) => {
    console.log(`[RCON] ${steamId} executed: ${command}`)
  },
  onBlocked: (steamId, command, reason) => {
    console.warn(`[RCON] Blocked ${steamId}: ${command} (${reason})`)
  },
})

// Will succeed for allowed users with matching permissions
await gated.execute('STEAM_0:1:12345678', 'say Hello everyone!')

// Will throw PermissionDeniedError
await gated.execute('STEAM_0:1:87654321', 'changelevel gm_flatgrass')

Error Handling

import {
  Rcon,
  AuthFailedError,
  ConnectionRefusedError,
  TimeoutError,
  DisconnectedError,
} from '@callowayisweird/source-rcon'

const rcon = new Rcon({
  host: '127.0.0.1',
  port: 27015,
  password: 'secret',
  timeout: 5000,
})

try {
  await rcon.connect()
} catch (err) {
  if (err instanceof AuthFailedError) {
    console.error('Wrong RCON password')
  } else if (err instanceof ConnectionRefusedError) {
    console.error('Server refused connection')
  } else if (err instanceof TimeoutError) {
    console.error('Connection timed out')
  }
}

try {
  await rcon.execute('status')
} catch (err) {
  if (err instanceof DisconnectedError) {
    console.error('Not connected to server')
  } else if (err instanceof TimeoutError) {
    console.error('Command timed out')
  }
}

API Reference

Rcon

| Method | Description | |---|---| | new Rcon(options) | Create a new RCON client | | connect() | Connect and authenticate | | execute(command) | Execute a command (queued, sequential) | | disconnect() | Disconnect from the server | | isConnected | Whether the client is authenticated |

RconOptions

| Option | Type | Default | Description | |---|---|---|---| | host | string | required | Server hostname or IP | | port | number | required | Server RCON port | | password | string | required | RCON password | | timeout | number | 5000 | Timeout in milliseconds | | autoReconnect | boolean | false | Auto-reconnect on disconnect | | maxReconnectAttempts | number | 5 | Max reconnection attempts | | reconnectDelay | number | 3000 | Delay between reconnect attempts (ms) |

Events

| Event | Arguments | Description | |---|---|---| | connect | none | Successfully connected and authenticated | | disconnect | none | Disconnected from server | | error | Error | An error occurred | | reconnecting | number | Attempting to reconnect (attempt number) |

RconPool

| Method | Description | |---|---| | new RconPool(servers) | Create pool from array of server options | | connectAll() | Connect to all servers | | execute(key, command) | Execute on a specific server (host:port) | | executeAll(command) | Execute on all servers | | disconnectAll() | Disconnect all servers | | get(key) | Get the Rcon instance for a server | | size | Number of servers in the pool |

GatedRcon

| Method | Description | |---|---| | new GatedRcon(rcon, options) | Create gated wrapper around an Rcon instance | | execute(steamId, command) | Execute if permitted, throws PermissionDeniedError if not |

Errors

| Error | Code | Description | |---|---|---| | RconError | varies | Base error class | | AuthFailedError | AUTH_FAILED | Wrong RCON password | | ConnectionError | CONNECTION_FAILED | Cannot connect to server | | ConnectionRefusedError | CONNECTION_REFUSED | Connection actively refused | | TimeoutError | TIMEOUT | Operation timed out | | DisconnectedError | DISCONNECTED | Not connected to server | | BannedError | BANNED | IP banned after failed auth attempts | | PermissionDeniedError | PERMISSION_DENIED | GatedRcon blocked the command |

License

MIT