Operator
local Operator = require(game:GetService("ServerStorage").Operator)The package entry point. This is the only require you need. Every public module and type is reachable from here or from the handle Start returns.
local Operator = require(game:GetService("ServerStorage").Operator)
local Command, Types = Operator.Command, Operator.TypesProperties
| Property | Type | Meaning |
|---|---|---|
Operator.Version | string | the package version, as shown in the Discord sink footer |
Operator.Command | Command | the command builder |
Operator.Type | Type | the argument type builder |
Operator.Types | Types | the nine built-in argument types |
Operator.Packs | Packs | the default command packs |
Operator.Audit | Audit | the audit log module |
Operator.ActionLog | ActionLog | the log panel's store |
Operator.Registry | Registry | command and type registration |
Operator.Dispatcher | Dispatcher | parsing, binding and running |
Operator.Roles | Roles | role resolution |
Operator.Log | Log | builds the logger Operator writes through |
Operator.SuggestionCache | SuggestionCache | caches an argument type's suggestions |
Operator.LogRecord | LogRecord | the log panel's record shapes |
Operator.Parsing | Parsing | Tokenizer, Parser, Binder |
Operator.Sinks | Discord | { Discord = ... } |
Start builds Audit, Registry, Dispatcher and Roles for you and puts the instances on the handle. Reach for the modules only when you are assembling the pieces yourself.
Internal paths are not public
Operator.Server.*, Operator.Shared.* and Operator.Client.* still resolve, and existing code that requires them keeps working. They are not part of the public API and may move in a minor release. Anything not in the table above is internal: Transport, Delivery, ClientReport, LogChannel, LocalDispatch, ClientContext, Protocol, Serializer, Util and Maid.
Types
Every type a consumer annotates against is exported from the root, so Operator. is the only prefix you need:
| Writing a command | Writing an argument type | Results and roles |
|---|---|---|
Builder<A> | ArgType<T> | Result |
Command | AnyArgType | Status |
Context | TypeBuilder<T> | RoleDefinition |
ClientContext | SuggestionOptions | RoleConfig |
Action | SuggestionCache | RoleProvider |
ActionTarget | RoleSet | |
GuardFn | ||
Argument, Flag, ArgOptions |
| Audit | Log panel | Config and handle | Adapters |
|---|---|---|---|
AuditRecord | LogPanelRecord | Config | Logger |
AuditQuery | LogPanelQuery | AuditConfig | LogSink |
AuditSink | LogPanelPage | LogsConfig | LogLevel |
AuditEntry | LogPanelStream | ClientConfig | SerializerAdapter |
AuditSource | ActionLog | Handle | |
Audit | Registry, Dispatcher, Roles |
local function onDenied(record: Operator.AuditRecord) end
local function myGuard(context: Operator.Context): (boolean, string?) endFunctions
Operator.Start
Operator.Start(config: Config?): HandleValidates the config, builds the registry, dispatcher, roles, audit and transport, and starts delivering the console. Returns the handle.
Idempotent. A second call logs a warning and returns the same handle. It never double-registers, double-mounts, or re-delivers a bundle.
Config is validated before anything is built. A misspelled key, a wrong-typed value or an unknown pack name raises an error naming the offending key and what was expected.
Operator.Start({
Commands = script.Parent.Commands,
DefaultCommands = { "debug", "moderation" },
Roles = {
owner = { UserIds = { 1234567 }, Inherits = { "moderator" } },
moderator = { GroupId = 7654321, MinRank = 200 },
},
})Operator.Start({}) is valid and gives a console for the place owner only.
Types
Config
type Config = {
Commands: Instance?,
ClientCommands: Instance?,
Types: Instance?,
Guards: Instance?,
DefaultCommands: { string }?,
Roles: RoleConfig?,
Audit: AuditConfig?,
Logs: LogsConfig?,
Client: ClientConfig?,
Logger: any?,
Serializer: any?,
}| Field | Default | Meaning |
|---|---|---|
Commands | none | an Instance whose descendant ModuleScripts are your commands |
ClientCommands | none | an Instance holding your client commands |
Types | none | your argument types; also delivered to authorised clients |
Guards | none | modules returning a guard function, or { guard, path } |
DefaultCommands | none | which built-in packs to enable |
Roles | place owner only | role definitions, see Roles |
Audit | enabled | see AuditConfig |
Logs | enabled | see LogsConfig |
Client | UI on, F2 | see ClientConfig |
Logger | warnings to output | a sink function, or a table with info/warn/error |
Serializer | pass-through | serialize / deserialize for remote payloads |
AuditConfig
type AuditConfig = {
Enabled: boolean?,
BufferSize: number?,
DiscordWebhook: any?,
DiscordSend: (string | (any) -> boolean)?,
DiscordFooterIcon: string?,
}LogsConfig
type LogsConfig = {
Enabled: boolean?,
BufferSize: number?,
Permission: string?,
}| Field | Default | Meaning |
|---|---|---|
Enabled | true | set false and the log panel reports logging is off |
BufferSize | 200 | moderation actions kept in memory |
Permission | none | a role required to read logs; by default, anyone who can run logs |
ClientConfig
type ClientConfig = {
UI: boolean?,
Mount: Instance?,
ActivationKey: EnumItem?,
Theme: (string | { [string]: Color3 })?,
HistoryLimit: number?,
Icon: (string | boolean)?,
}| Field | Default | Meaning |
|---|---|---|
UI | true | whether the built-in console is delivered |
Mount | none | a ModuleScript of yours to mount instead; implies UI = false |
ActivationKey | Enum.KeyCode.F2 | the toggle key |
Theme | "signal" | signal, operator, iris, or a table of token overrides |
HistoryLimit | 500 | console lines kept; 0 means uncapped |
Icon | the >_ glyph | the topbar icon image, or false for none |
Setting both Mount and UI = true is an error rather than a silent choice between them.
Handle
What Start returns.
| Member | Type | Use |
|---|---|---|
registry | Registry | register commands or types after startup |
dispatcher | Dispatcher | run commands from server code, add guards |
roles | Roles | query or invalidate a player's roles |
audit | Audit? | query the log, add sinks; nil when Audit.Enabled is false |
logs | ActionLog? | the log panel store; nil when Logs.Enabled is false |
transport | Transport | delivery internals; prefer refresh and refreshAll |
refresh(player) | (Player) -> () | re-evaluate access for one player |
refreshAll() | () -> () | re-evaluate everyone |
stop() | () -> () | tear everything down |
local operator = Operator.Start({ DefaultCommands = { "debug" } })
operator.registry:registerCommand(myCommand)
operator.refresh(player)stop() stops the transport, destroys the roles cache and closes the audit sinks, and lets a later Start run again.
Packs
Packs.names(): { string }
Packs.load(registry: Registry, names: { string }, services: Services?): numbertype Services = {
registry: any,
canRun: ((player: Player, command: any) -> boolean)?,
}Packs.load returns how many commands it registered. canRun is only used by help, so it can list just the commands that player may run. Naming a pack that does not exist is an error listing the ones that do, and nothing is registered.
Most setups never call this directly. DefaultCommands on Start does it for you.
Exported types
Re-exported so a consumer never needs an internal path:
| Type | From |
|---|---|
Builder<A>, Command, Context, GuardFn, Argument, Flag, ArgOptions | Command |
ArgType<T>, AnyArgType, TypeBuilder<T>, SuggestionOptions | Type |
Result, Status | Dispatcher |
LogsConfig | this page |
RoleDefinition, RoleConfig, RoleProvider | Roles |
Internal paths are not public
Operator.Server.* and Operator.Shared.* are reachable but not part of the public surface, and may move in a minor release. The public surface is what require(Operator) returns.