Skip to content

CalmProton/SquadScript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

69 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

SquadScript

A modern, type-safe server management framework for Squad game servers.

GitHub release GitHub contributors GitHub license GitHub issues GitHub stars


Built with TypeScript and Bun for optimal performance, SquadScript provides robust RCON management, log parsing, and an extensible plugin system.


About

SquadScript is a scripting framework designed for Squad servers that handles all communication and data collection to and from your servers. It allows you to easily manage your server with powerful plugins without having to worry about the hassle of RCON or log parsing. SquadScript comes with multiple plugins already built for you, allowing you to experience its power right away.

SquadScript must be hosted on the same server box as your Squad server, or be connected to your Squad server via FTP/SFTP for log access.

Features

  • ๐Ÿ”’ Type-Safe - Full TypeScript with compile-time type checking
  • ๐Ÿš€ Fast - Built on Bun runtime for optimal performance
  • ๐Ÿ”Œ Extensible Plugin System - Easy to create and share plugins
  • ๐Ÿ“ก Multiple Log Readers - Support for local, FTP, and SFTP log access
  • ๐ŸŽฎ Rich Event System - React to player events, kills, chat, and more
  • ๐Ÿค– Discord Integration - Built-in Discord plugins for chat relay, admin alerts, and more
  • ๐Ÿ“Š Community Ban List (CBL) - Integration for checking player reputation
  • โšก Result-Based Error Handling - Explicit error handling without exceptions

Table of Contents


Requirements

  • Bun v1.3.0 or higher - Download
  • Squad Server with RCON enabled
  • Discord Bot Token (optional, for Discord plugins)

Installation

  1. Download SquadScript and unzip the download.
  2. Open the unzipped folder in your terminal.
  3. Install the dependencies:
    bun install
  4. Build the packages:
    bun run build
  5. Configure the config.json file. See Configuration for details.
  6. Start SquadScript:
    bun run start

Note: If you want to test the latest development version, clone the main branch directly instead of downloading a release.

Quick Start

1. Configure Your Server

Edit the config.json file in the root directory:

{
  "servers": [
    {
      "id": 1,
      "name": "My Squad Server",
      "rcon": {
        "host": "127.0.0.1",
        "port": 21114,
        "password": "your-rcon-password"
      },
      "logReader": {
        "mode": "tail",
        "logDir": "C:/servers/squad_server/SquadGame/Saved/Logs"
      },
      "plugins": [
        {
          "plugin": "SeedingMode",
          "enabled": true,
          "options": {
            "seedingThreshold": 50,
            "seedingMessage": "Seeding Rules Active! Fight only over the middle flags!"
          }
        },
        {
          "plugin": "AutoTKWarn",
          "enabled": true
        }
      ]
    }
  ]
}

2. Start SquadScript

bun run start

That's it! SquadScript will connect to your server and start running the enabled plugins.


Configuration

The config file needs to be valid JSON syntax. If an error is thrown saying the config cannot be parsed, try putting the config into a JSON syntax checker.

Server Configuration

The main configuration file supports single or multi-server setups:

{
  "servers": [
    {
      "id": 1,
      "name": "Server Display Name",
      "rcon": { /* RCON config */ },
      "logReader": { /* Log reader config */ },
      "adminLists": [ /* Admin list sources */ ],
      "connectors": { /* Connector configs (Discord, etc.) */ },
      "plugins": [ /* Plugin configurations */ ],
      "verbosity": { /* Logging verbosity */ }
    }
  ],
  "connectors": { /* Global connectors shared across servers */ },
  "verbosity": { /* Global verbosity settings */ }
}
Option Description
id An integer ID to uniquely identify the server
name Human-readable server name
rcon RCON connection configuration
logReader Log reader configuration
adminLists Sources for identifying admins on the server
connectors Server-specific connector configurations
plugins Array of plugin configurations
verbosity Logging verbosity settings

RCON Settings

{
  "rcon": {
    "host": "127.0.0.1",
    "port": 21114,
    "password": "your-rcon-password",
    "autoReconnect": true,
    "autoReconnectDelay": 5000,
    "maxReconnectAttempts": 0,
    "timeout": 10000
  }
}
Option Type Default Description
host string - The IP of the server
port number 21114 The RCON port of the server
password string - The RCON password of the server
autoReconnect boolean true Auto-reconnect on disconnect
autoReconnectDelay number 5000 Delay between reconnection attempts (ms)
maxReconnectAttempts number 0 Max reconnection attempts (0 = infinite)
timeout number 10000 Connection timeout (ms)

Log Reader

SquadScript supports three modes for reading server logs:

Local Tail Mode

Use tail mode when SquadScript is hosted on the same machine as your Squad server.

{
  "logReader": {
    "mode": "tail",
    "logDir": "C:/servers/squad_server/SquadGame/Saved/Logs",
    "filename": "SquadGame.log"
  }
}

FTP Mode

Use ftp mode to read logs from a remote server via FTP.

{
  "logReader": {
    "mode": "ftp",
    "logDir": "/remote/path/to/logs",
    "ftp": {
      "host": "ftp.example.com",
      "port": 21,
      "username": "your-username",
      "password": "your-password",
      "secure": false
    }
  }
}

SFTP Mode

Use sftp mode for secure remote log access.

{
  "logReader": {
    "mode": "sftp",
    "logDir": "/remote/path/to/logs",
    "ftp": {
      "host": "sftp.example.com",
      "port": 22,
      "username": "your-username",
      "password": "your-password"
    }
  }
}

Admin Lists

Load admin permissions from multiple sources:

{
  "adminLists": [
    {
      "type": "local",
      "source": "C:/servers/squad_server/SquadGame/ServerConfig/Admins.cfg"
    },
    {
      "type": "remote",
      "source": "https://example.com/admins.txt",
      "refreshInterval": 300000
    },
    {
      "type": "ftp",
      "source": "/remote/path/Admins.cfg",
      "refreshInterval": 300000
    }
  ]
}
Type Description
local Read from a local file path
remote Fetch from a URL
ftp Read from an FTP/SFTP server

Connectors

Connectors allow SquadScript to communicate with external resources. They should be named and configured, then referenced by plugins.

Discord Connector

{
  "connectors": {
    "discord": {
      "type": "discord",
      "token": "your-discord-bot-token",
      "guildId": "your-guild-id"
    }
  }
}

Requires a Discord bot login token. Create a bot at the Discord Developer Portal.

Plugins

Plugins are configured in the plugins array. Each plugin can be enabled/disabled and configured with options.

{
  "plugins": [
    {
      "plugin": "PluginName",
      "enabled": true,
      "options": {
        "option1": "value1",
        "option2": 123
      },
      "connectors": {
        "discordClient": "discord"
      }
    }
  ]
}
Field Description
plugin The name of the plugin
enabled Toggle true/false to enable/disable the plugin
options Plugin-specific configuration options
connectors Map connector names to the plugin (for Discord plugins, etc.)

Verbosity

Control console output verbosity for different modules:

{
  "verbosity": {
    "SquadServer": 1,
    "RconClient": 1,
    "LogParser": 1,
    "PluginManager": 2
  }
}

Higher numbers produce more verbose output.


Available Plugins

The following is a list of plugins built into SquadScript. Click to expand for configuration details.

Core Plugins

ChatCommands - Listen for !command patterns in chat

Listen for !command patterns in chat and respond with preset messages.

{
  "plugin": "ChatCommands",
  "enabled": true,
  "options": {
    "commands": [
      { "command": "rules", "type": "warn", "response": "Please follow server rules!" },
      { "command": "discord", "type": "broadcast", "response": "Join our Discord: discord.gg/example" }
    ]
  }
}
Option Description Default
commands Array of command definitions []
commands[].command Command trigger (without ! prefix) -
commands[].type warn (to player) or broadcast (to all) -
commands[].response Message to send -
AutoTKWarn - Automatically warn players when they teamkill

Sends warning messages to players when they teamkill.

{
  "plugin": "AutoTKWarn",
  "enabled": true,
  "options": {
    "attackerMessage": "Please apologise for ALL TKs in ALL chat!",
    "victimMessage": "You were teamkilled. The player has been warned."
  }
}
Option Description Default
attackerMessage Message sent to the team killer "Please apologise for ALL TKs in ALL chat!"
victimMessage Message sent to the victim (null to disable) null
SeedingMode - Broadcast seeding rules when below player threshold

Broadcasts seeding rule messages when the server is below a player count threshold.

{
  "plugin": "SeedingMode",
  "enabled": true,
  "options": {
    "seedingThreshold": 50,
    "seedingMessage": "Seeding Rules Active! Fight only over the middle flags!",
    "liveEnabled": true,
    "liveThreshold": 52,
    "liveMessage": "Server is now LIVE! Normal rules apply.",
    "interval": 150000,
    "waitOnNewGames": true,
    "waitTimeOnNewGame": 30000
  }
}
Option Description Default
seedingThreshold Player count below which seeding rules are active 50
seedingMessage Message broadcast when seeding "Seeding Rules Active!..."
liveEnabled Enable "Live" announcements true
liveThreshold Player count for "Live" message 52
liveMessage Message when server goes live "Server is now LIVE! Normal rules apply."
interval Broadcast interval (ms) 150000
waitOnNewGames Pause broadcasts after new game starts true
waitTimeOnNewGame How long to pause (ms) 30000
IntervalledBroadcasts - Broadcast rotating messages at intervals

Broadcasts messages from a list in rotation at regular intervals.

{
  "plugin": "IntervalledBroadcasts",
  "enabled": true,
  "options": {
    "broadcasts": [
      "Welcome to our server! Follow the rules.",
      "Join our Discord: discord.gg/example",
      "Report issues to admins using !admin"
    ],
    "interval": 300000
  }
}
Option Description Default
broadcasts Array of messages to broadcast []
interval Interval between broadcasts (ms) 300000
AutoKickUnassigned - Kick players who remain unassigned

Automatically kicks players who remain unassigned (not in a squad) for too long.

{
  "plugin": "AutoKickUnassigned",
  "enabled": true,
  "options": {
    "warningMessage": "Join a squad, you are unassigned and will be kicked",
    "kickMessage": "Unassigned - automatically removed",
    "frequencyOfWarnings": 30000,
    "unassignedTimer": 360000,
    "playerThreshold": 93,
    "roundStartDelay": 900000,
    "ignoreAdmins": false,
    "ignoreWhitelist": false
  }
}
Option Description Default
warningMessage Warning message sent to unassigned players "Join a squad..."
kickMessage Kick reason shown to the player "Unassigned - automatically removed"
frequencyOfWarnings Interval between warnings (ms) 30000
unassignedTimer Time before kick (ms) 360000
playerThreshold Minimum player count for auto-kick (-1 to disable) 93
roundStartDelay Grace period after round start (ms) 900000
ignoreAdmins Exempt admins from auto-kick false
ignoreWhitelist Exempt reserved slot players false
FogOfWar - Set fog of war mode at round start

Automatically sets fog of war mode when a new game starts.

{
  "plugin": "FogOfWar",
  "enabled": true,
  "options": {
    "mode": 1,
    "delay": 10000
  }
}
Option Description Default
mode Fog of war mode to set 1
delay Delay after round start (ms) 10000
TeamRandomizer - Randomize team assignments via admin command

Provides an admin command to randomize team assignments.

{
  "plugin": "TeamRandomizer",
  "enabled": true
}

Entertainment & QoL Plugins

FirstBlood - Announce the first kill of each round
{
  "plugin": "FirstBlood",
  "enabled": true,
  "options": {
    "message": "{attacker} drew first blood against {victim}!"
  }
}
PlayerWelcome - Welcome players when they join
{
  "plugin": "PlayerWelcome",
  "enabled": true,
  "options": {
    "newPlayerMessage": "Welcome to the server, {player}!"
  }
}
RevengeTracker - Track and announce revenge kills
{
  "plugin": "RevengeTracker",
  "enabled": true
}
RoundStatsSummary - Display round statistics at match end
{
  "plugin": "RoundStatsSummary",
  "enabled": true
}

Squad Management Plugins

SquadNameEnforcer - Enforce squad naming conventions
{
  "plugin": "SquadNameEnforcer",
  "enabled": true,
  "options": {
    "blockedWords": ["offensive", "words"],
    "warnMessage": "Please rename your squad appropriately"
  }
}
SquadLeaderChangeAlert - Alert when squad leaders change
{
  "plugin": "SquadLeaderChangeAlert",
  "enabled": true
}
OrphanSquadLogger - Log squads that become leaderless
{
  "plugin": "OrphanSquadLogger",
  "enabled": true
}
VehicleClaimTracker - Track vehicle claims by squads
{
  "plugin": "VehicleClaimTracker",
  "enabled": true
}

Discord Plugins

Note: All Discord plugins require a Discord connector to be configured. See Connectors.

DiscordChat - Relay chat between in-game and Discord
{
  "plugin": "DiscordChat",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "ignoreChats": ["ChatSquad"],
    "color": 16766720
  },
  "connectors": { "discordClient": "discord" }
}
Option Description Default
channelID Discord channel ID for chat relay Required
ignoreChats Chat channels to ignore []
color Default embed color 0xffd700 (gold)
chatColors Colors for specific chat channels {}
DiscordTeamkill - Post teamkill alerts to Discord
{
  "plugin": "DiscordTeamkill",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "color": 16711680,
    "includeCBL": true
  },
  "connectors": { "discordClient": "discord" }
}
DiscordAdminRequest - Forward admin requests to Discord
{
  "plugin": "DiscordAdminRequest",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "command": "admin",
    "pingGroups": ["987654321098765432"],
    "pingHere": false,
    "pingDelay": 60000,
    "color": 16761867
  },
  "connectors": { "discordClient": "discord" }
}
DiscordServerStatus - Post server status updates
{
  "plugin": "DiscordServerStatus",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "updateInterval": 60000,
    "showNextLayer": true
  },
  "connectors": { "discordClient": "discord" }
}
DiscordAdminBroadcast - Relay admin broadcasts to Discord
{
  "plugin": "DiscordAdminBroadcast",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "color": 16761867
  },
  "connectors": { "discordClient": "discord" }
}
DiscordAdminCamLogs - Log admin camera usage
{
  "plugin": "DiscordAdminCamLogs",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "color": 16761867
  },
  "connectors": { "discordClient": "discord" }
}
DiscordKillFeed - Post kill feed to Discord
{
  "plugin": "DiscordKillFeed",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "includeWounds": false,
    "includeTeamkillsOnly": false
  },
  "connectors": { "discordClient": "discord" }
}
DiscordRoundWinner - Announce round winners
{
  "plugin": "DiscordRoundWinner",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "color": 16761867
  },
  "connectors": { "discordClient": "discord" }
}
DiscordSquadCreated - Announce new squads
{
  "plugin": "DiscordSquadCreated",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "color": 3329330
  },
  "connectors": { "discordClient": "discord" }
}

Advanced Plugins

CBLInfo - Check players against Community Ban List

Alerts admins when players with Community Ban List (CBL) reputation join.

{
  "plugin": "CBLInfo",
  "enabled": true,
  "options": {
    "channelID": "123456789012345678",
    "threshold": 1,
    "pingGroups": ["987654321098765432"],
    "pingHere": false,
    "color": 16729156
  },
  "connectors": { "discordClient": "discord" }
}
Option Description Default
channelID Discord channel ID for CBL alerts Required
threshold Minimum reputation points to trigger alert (0 = any record) 1
pingGroups Discord role IDs to ping on alerts []
pingHere Use @here ping on alerts false
DBLog - Log events to a database

Logs server statistics and events to a database for analytics and stat tracking.

{
  "plugin": "DBLog",
  "enabled": true,
  "options": {
    "database": "mysql",
    "serverID": 1
  }
}
SocketIOAPI - Real-time API via Socket.IO

Provides a real-time API for external applications.

{
  "plugin": "SocketIOAPI",
  "enabled": true,
  "options": {
    "port": 3000,
    "authentication": "your-secret-token"
  }
}
Option Description Default
port Port for the Socket.IO server 3000
authentication Security token for API access Required

Complete Configuration Example

Click to expand full config.json example
{
  "servers": [
    {
      "id": 1,
      "name": "My Community Squad Server",
      "rcon": {
        "host": "game.example.com",
        "port": 21114,
        "password": "secure-rcon-password"
      },
      "logReader": {
        "mode": "sftp",
        "logDir": "/home/squad/SquadGame/Saved/Logs",
        "ftp": {
          "host": "game.example.com",
          "port": 22,
          "username": "squad",
          "password": "sftp-password"
        }
      },
      "adminLists": [
        { "type": "local", "source": "/etc/squad/Admins.cfg" },
        { "type": "remote", "source": "https://api.example.com/admins.txt" }
      ],
      "connectors": {
        "discord": {
          "type": "discord",
          "token": "your-discord-bot-token"
        }
      },
      "plugins": [
        {
          "plugin": "SeedingMode",
          "enabled": true,
          "options": {
            "seedingThreshold": 40,
            "seedingMessage": "๐ŸŒฑ Seeding Rules: Fight over middle objectives only!",
            "liveThreshold": 50,
            "liveMessage": "๐ŸŽฎ Server is LIVE! All rules apply!"
          }
        },
        {
          "plugin": "AutoTKWarn",
          "enabled": true,
          "options": {
            "attackerMessage": "โš ๏ธ You teamkilled! Apologize in ALL chat immediately!"
          }
        },
        {
          "plugin": "AutoKickUnassigned",
          "enabled": true,
          "options": {
            "playerThreshold": 80,
            "unassignedTimer": 300000
          }
        },
        {
          "plugin": "IntervalledBroadcasts",
          "enabled": true,
          "options": {
            "broadcasts": [
              "๐Ÿ“ข Welcome! Server rules: No intentional TK, respect admins.",
              "๐Ÿ’ฌ Join our Discord: discord.gg/example",
              "๐Ÿ›ก๏ธ Need an admin? Use !admin in chat"
            ],
            "interval": 600000
          }
        },
        {
          "plugin": "ChatCommands",
          "enabled": true,
          "options": {
            "commands": [
              { "command": "rules", "type": "warn", "response": "1. No TK 2. Respect others 3. Follow admin instructions" },
              { "command": "discord", "type": "warn", "response": "Join us: discord.gg/example" }
            ]
          }
        },
        {
          "plugin": "DiscordChat",
          "enabled": true,
          "options": { "channelID": "111111111111111111" },
          "connectors": { "discordClient": "discord" }
        },
        {
          "plugin": "DiscordTeamkill",
          "enabled": true,
          "options": { "channelID": "222222222222222222" },
          "connectors": { "discordClient": "discord" }
        },
        {
          "plugin": "DiscordAdminRequest",
          "enabled": true,
          "options": {
            "channelID": "333333333333333333",
            "pingGroups": ["444444444444444444"]
          },
          "connectors": { "discordClient": "discord" }
        },
        {
          "plugin": "CBLInfo",
          "enabled": true,
          "options": {
            "channelID": "555555555555555555",
            "threshold": 3,
            "pingGroups": ["444444444444444444"]
          },
          "connectors": { "discordClient": "discord" }
        }
      ]
    }
  ]
}

Creating Custom Plugins

Interested in creating your own plugin? SquadScript makes it easy with a type-safe plugin API.

Click to expand custom plugin guide

Create custom plugins by extending the BasePlugin class:

import { BasePlugin } from '@squadscript/server';
import type { OptionsSpec, PluginMeta } from '@squadscript/types';

const optionsSpec = {
  greeting: {
    type: 'string',
    required: false,
    description: 'Greeting message for players',
    default: 'Welcome to the server!',
  },
} as const satisfies OptionsSpec;

export class MyCustomPlugin extends BasePlugin<typeof optionsSpec> {
  static readonly meta: PluginMeta = {
    name: 'MyCustomPlugin',
    description: 'A custom plugin example',
    version: '1.0.0',
    defaultEnabled: true,
  };

  static readonly optionsSpec = optionsSpec;

  async mount(): Promise<void> {
    this.on('PLAYER_CONNECTED', async (event) => {
      this.log.info(`Player connecting: ${event.player.eosID}`);
    });

    this.on('PLAYER_JOIN_SUCCEEDED', async (event) => {
      await this.rcon.warn(event.player.eosID, this.options.greeting);
      this.log.info(`Welcomed player: ${event.player.name}`);
    });
  }
}

Plugin Lifecycle

  1. Constructor - Plugin is instantiated with options
  2. prepareToMount() - Validate options, setup connectors
  3. mount() - Subscribe to events, start intervals
  4. unmount() - Cleanup resources, clear intervals

Available APIs in Plugins

// Logging
this.log.info('Information message');
this.log.warn('Warning message');
this.log.error('Error message', new Error('details'));
this.log.debug('Debug message');
this.log.verbose('Verbose message');

// RCON Commands
await this.rcon.warn(eosID, 'Message');
await this.rcon.kick(eosID, 'Reason');
await this.rcon.ban(steamID, '1d', 'Reason');  // (target, duration, reason)
await this.rcon.broadcast('Server-wide message');
await this.rcon.execute('RawCommand');

// Server State
const players = this.server.players;
const squads = this.server.squads;
const currentLayer = this.server.currentLayer;

// Event Subscriptions
this.on('EVENT_NAME', async (event) => { /* handler */ });

// Timers (auto-cleanup on unmount)
this.setTimeout(() => { /* code */ }, delay);
this.setInterval(() => { /* code */ }, interval);

// Connectors
const discord = this.getConnector<DiscordConnector>('discord');

Troubleshooting

RCON Connection Issues
  1. Verify RCON is enabled in your server's Server.cfg
  2. Check firewall rules allow the RCON port (default: 21114)
  3. Verify credentials are correct in your config
  4. Check server is running before starting SquadScript
Log Reader Issues

Local tail mode:

  • Ensure the log directory path is correct
  • Verify file permissions allow reading

FTP/SFTP mode:

  • Test connection with an FTP client first
  • Check firewall allows outbound connections
  • Verify credentials and paths
Plugin Not Loading
  1. Check plugin name is spelled correctly
  2. Verify enabled: true in the config
  3. Check for required options
  4. Review logs for error messages
Discord Bot Not Working
  1. Verify bot token is valid
  2. Ensure bot has proper permissions in the server
  3. Check channel IDs are correct (right-click channel โ†’ Copy ID)
  4. Verify the bot is in the correct guild
Common Errors
Error Solution
RCON connection refused Check host, port, and firewall
Authentication failed Verify RCON password
Log file not found Check log directory path
Discord connector not found Configure Discord connector before Discord plugins
Permission denied Check file permissions for log reader
Config cannot be parsed Validate JSON syntax with a JSON checker

Statement on Accuracy

Some information SquadScript collects from Squad servers was never intended or designed to be collected. As a result, it is impossible for any framework to collect the same information with 100% accuracy. SquadScript aims to get as close as possible to that figure, however, it acknowledges that this is not possible in some specific scenarios.

Known limitations:

  • Real-time data - Server and player information is updated periodically (default: every 30 seconds). Some information may be slightly out of date.
  • Restarts during active games - If SquadScript is started during an active game, some player state information may be incomplete.
  • Duplicate player names - If multiple players have the same name, SquadScript may not be able to uniquely identify them in some log events.

Project Structure

SquadScript/
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ config/       # Configuration schemas and loaders
โ”‚   โ”œโ”€โ”€ log-parser/   # Squad log file parser
โ”‚   โ”œโ”€โ”€ logger/       # Logging utilities
โ”‚   โ”œโ”€โ”€ rcon/         # RCON client implementation
โ”‚   โ””โ”€โ”€ types/        # Shared TypeScript types
โ”œโ”€โ”€ projects/
โ”‚   โ”œโ”€โ”€ core/         # Main SquadServer class and plugin system
โ”‚   โ””โ”€โ”€ plugins/      # Official plugin collection
โ”œโ”€โ”€ config.json       # Your server configuration (create this)
โ””โ”€โ”€ package.json      # Workspace root

Development

# Run tests
bun test

# Type check
bun run check-types

# Lint
bun run lint

# Build all packages
bun run build

License

MIT

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a pull request.

Support


Built with โค๏ธ for the Squad community

About

Modern SquadJS alternative powered by Bun

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages