@minecraft-skills/rcon 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 sya-ri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @minecraft-skills/rcon
2
+
3
+ RCON configuration, permission, and execution utilities for minecraft-skills.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pnpm add @minecraft-skills/rcon
9
+ ```
10
+
11
+ Node.js 22.12 or newer is required.
12
+
13
+ ## Examples
14
+
15
+ ```ts
16
+ import {
17
+ createRconConfig,
18
+ evaluateRconPermission,
19
+ getRconConfigStatus,
20
+ runRconCommand,
21
+ } from "@minecraft-skills/rcon";
22
+
23
+ createRconConfig({
24
+ configPath: "./.minecraft-skills/rcon.json",
25
+ preset: "readonly",
26
+ });
27
+
28
+ const status = getRconConfigStatus({
29
+ configPath: "./.minecraft-skills/rcon.json",
30
+ });
31
+
32
+ const decision = evaluateRconPermission("list");
33
+
34
+ const result = await runRconCommand({
35
+ command: "list",
36
+ configPath: "./.minecraft-skills/rcon.json",
37
+ });
38
+ ```
39
+
40
+ `runRconCommand` sends the command only when the selected profile is configured and the regex
41
+ permissions allow it. Rejected commands return a permission decision without contacting the server.
42
+
43
+ ## Configuration
44
+
45
+ Secrets should stay in environment variables. Config values can use `$env:NAME`:
46
+
47
+ ```json
48
+ {
49
+ "$schema": "https://raw.githubusercontent.com/sya-ri/minecraft-skills/main/schema/rcon-config.schema.json",
50
+ "defaultProfile": "local",
51
+ "profiles": {
52
+ "local": {
53
+ "host": "127.0.0.1",
54
+ "port": 25575,
55
+ "password": "$env:MINECRAFT_SKILLS_RCON_PASSWORD",
56
+ "permissions": {
57
+ "preset": "readonly"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ See the repository `docs/RCON.md` for config search order, permission presets, CLI usage, and MCP
65
+ behavior.
@@ -0,0 +1,96 @@
1
+ //#region src/index.d.ts
2
+ type RconPermissionPreset = "readonly" | "guarded" | "full";
3
+ type RconPermissionMode = "allow" | "deny";
4
+ type RconPermissions = {
5
+ preset?: RconPermissionPreset;
6
+ defaultMode?: RconPermissionMode;
7
+ allow?: string[];
8
+ deny?: string[];
9
+ };
10
+ type RconProfileConfig = {
11
+ host: string;
12
+ port: number | string;
13
+ password: string;
14
+ timeoutMs?: number | string;
15
+ permissions?: RconPermissions;
16
+ };
17
+ type RconConfig = {
18
+ $schema?: string;
19
+ defaultProfile?: string;
20
+ profiles: Record<string, RconProfileConfig>;
21
+ };
22
+ type RconConfigStatusOptions = {
23
+ configPath?: string;
24
+ profile?: string;
25
+ };
26
+ type RconConfigStatus = {
27
+ configured: boolean;
28
+ configPath: string | null;
29
+ source: "explicit" | "env-config" | "local" | "user" | "env-profile" | "missing";
30
+ profile: string | null;
31
+ missing: string[];
32
+ warnings: string[];
33
+ message: string;
34
+ };
35
+ type RconCreateConfigOptions = {
36
+ configPath?: string;
37
+ profile?: string;
38
+ host?: string;
39
+ port?: number | string;
40
+ passwordEnv?: string;
41
+ preset?: RconPermissionPreset;
42
+ force?: boolean;
43
+ };
44
+ type RconCreateConfigResult = {
45
+ path: string;
46
+ existed: boolean;
47
+ written: boolean;
48
+ warning: string | null;
49
+ config: RconConfig;
50
+ };
51
+ type RconCommandOptions = {
52
+ command: string;
53
+ configPath?: string;
54
+ profile?: string;
55
+ timeoutMs?: number;
56
+ };
57
+ type RconPermissionDecision = {
58
+ allowed: boolean;
59
+ defaultMode: RconPermissionMode;
60
+ matchedRule: {
61
+ source: "allow" | "deny" | "default";
62
+ pattern: string | null;
63
+ };
64
+ normalizedCommand: string;
65
+ };
66
+ type RconCommandResult = {
67
+ profile: string;
68
+ host: string;
69
+ port: number;
70
+ command: string;
71
+ response: string;
72
+ permissionDecision: RconPermissionDecision;
73
+ };
74
+ type ResolvedRconProfile = {
75
+ configPath: string | null;
76
+ source: RconConfigStatus["source"];
77
+ profile: string;
78
+ host: string;
79
+ port: number;
80
+ password: string;
81
+ timeoutMs: number;
82
+ permissions: Required<RconPermissions>;
83
+ warnings: string[];
84
+ };
85
+ type RconTransport = (profile: ResolvedRconProfile, command: string) => Promise<string>;
86
+ declare function resolveRconPermissions(permissions?: RconPermissions): Required<RconPermissions>;
87
+ declare function evaluateRconPermission(command: string, permissions?: RconPermissions): RconPermissionDecision;
88
+ declare function defaultRconConfigPath(): string;
89
+ declare function getRconConfigStatus(options?: RconConfigStatusOptions): RconConfigStatus;
90
+ declare function isRconConfigured(options?: RconConfigStatusOptions): boolean;
91
+ declare function createRconConfig(options?: RconCreateConfigOptions): RconCreateConfigResult;
92
+ declare function sendRconCommand(profile: ResolvedRconProfile, command: string): Promise<string>;
93
+ declare function runRconCommand(options: RconCommandOptions, transport?: RconTransport): Promise<RconCommandResult>;
94
+ //#endregion
95
+ export { RconCommandOptions, RconCommandResult, RconConfig, RconConfigStatus, RconConfigStatusOptions, RconCreateConfigOptions, RconCreateConfigResult, RconPermissionDecision, RconPermissionMode, RconPermissionPreset, RconPermissions, RconProfileConfig, RconTransport, ResolvedRconProfile, createRconConfig, defaultRconConfigPath, evaluateRconPermission, getRconConfigStatus, isRconConfigured, resolveRconPermissions, runRconCommand, sendRconCommand };
96
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";KAKY,oBAAA;AAAA,KACA,kBAAA;AAAA,KAEA,eAAA;EACV,MAAA,GAAS,oBAAA;EACT,WAAA,GAAc,kBAAkB;EAChC,KAAA;EACA,IAAA;AAAA;AAAA,KAGU,iBAAA;EACV,IAAA;EACA,IAAA;EACA,QAAA;EACA,SAAA;EACA,WAAA,GAAc,eAAe;AAAA;AAAA,KAGnB,UAAA;EACV,OAAA;EACA,cAAA;EACA,QAAA,EAAU,MAAM,SAAS,iBAAA;AAAA;AAAA,KAGf,uBAAA;EACV,UAAA;EACA,OAAO;AAAA;AAAA,KAGG,gBAAA;EACV,UAAA;EACA,UAAA;EACA,MAAA;EACA,OAAA;EACA,OAAA;EACA,QAAA;EACA,OAAA;AAAA;AAAA,KAGU,uBAAA;EACV,UAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;EACA,WAAA;EACA,MAAA,GAAS,oBAAoB;EAC7B,KAAA;AAAA;AAAA,KAGU,sBAAA;EACV,IAAA;EACA,OAAA;EACA,OAAA;EACA,OAAA;EACA,MAAA,EAAQ,UAAU;AAAA;AAAA,KAGR,kBAAA;EACV,OAAA;EACA,UAAA;EACA,OAAA;EACA,SAAA;AAAA;AAAA,KAGU,sBAAA;EACV,OAAA;EACA,WAAA,EAAa,kBAAkB;EAC/B,WAAA;IACE,MAAA;IACA,OAAA;EAAA;EAEF,iBAAA;AAAA;AAAA,KAGU,iBAAA;EACV,OAAA;EACA,IAAA;EACA,IAAA;EACA,OAAA;EACA,QAAA;EACA,kBAAA,EAAoB,sBAAsB;AAAA;AAAA,KAGhC,mBAAA;EACV,UAAA;EACA,MAAA,EAAQ,gBAAA;EACR,OAAA;EACA,IAAA;EACA,IAAA;EACA,QAAA;EACA,SAAA;EACA,WAAA,EAAa,QAAA,CAAS,eAAA;EACtB,QAAA;AAAA;AAAA,KAGU,aAAA,IAAiB,OAAA,EAAS,mBAAA,EAAqB,OAAA,aAAoB,OAAO;AAAA,iBA0GtE,sBAAA,CAAuB,WAAA,GAAc,eAAA,GAAkB,QAAA,CAAS,eAAA;AAAA,iBA0BhE,sBAAA,CACd,OAAA,UACA,WAAA,GAAc,eAAA,GACb,sBAAsB;AAAA,iBAyCT,qBAAA;AAAA,iBA8NA,mBAAA,CAAoB,OAAA,GAAS,uBAAA,GAA+B,gBAAgB;AAAA,iBAI5E,gBAAA,CAAiB,OAAqC,GAA5B,uBAA4B;AAAA,iBAItD,gBAAA,CAAiB,OAAA,GAAS,uBAAA,GAA+B,sBAAsB;AAAA,iBAiIzE,eAAA,CACpB,OAAA,EAAS,mBAAA,EACT,OAAA,WACC,OAAO;AAAA,iBAoDY,cAAA,CACpB,OAAA,EAAS,kBAAA,EACT,SAAA,GAAW,aAAA,GACV,OAAA,CAAQ,iBAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,491 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { Socket } from "node:net";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ //#region src/index.ts
6
+ const defaultRconPort = 25575;
7
+ const defaultRconTimeoutMs = 2e3;
8
+ const readonlyPermissions = {
9
+ preset: "readonly",
10
+ defaultMode: "deny",
11
+ allow: [
12
+ "^list$",
13
+ "^help(?: .+)?$",
14
+ "^minecraft:help(?: .+)?$",
15
+ "^version$",
16
+ "^plugins$",
17
+ "^pl$",
18
+ "^bukkit:version$",
19
+ "^bukkit:plugins$",
20
+ "^time query (?:daytime|gametime|day)$",
21
+ "^difficulty$",
22
+ "^worldborder get$",
23
+ "^gamerule [A-Za-z0-9_.:-]+$",
24
+ "^datapack list(?: .+)?$",
25
+ "^team list(?: .+)?$",
26
+ "^scoreboard objectives list$",
27
+ "^scoreboard players list(?: .+)?$",
28
+ "^scoreboard players get .+$",
29
+ "^bossbar list$",
30
+ "^bossbar get .+$",
31
+ "^attribute .+ get .+$",
32
+ "^(?:experience|xp) query .+$",
33
+ "^data get .+$",
34
+ "^tag .+ list$",
35
+ "^forceload query(?: .+)?$"
36
+ ],
37
+ deny: []
38
+ };
39
+ const guardedPermissions = {
40
+ preset: "guarded",
41
+ defaultMode: "allow",
42
+ allow: [],
43
+ deny: [
44
+ "(?:^|\\s)(?:minecraft:)?(?:stop|restart)(?:$|\\s)",
45
+ "(?:^|\\s)(?:minecraft:)?save-[A-Za-z-]+(?:$|\\s)",
46
+ "(?:^|\\s)(?:minecraft:)?(?:op|deop|ban|ban-ip|pardon|pardon-ip|kick)(?:$|\\s)",
47
+ "(?:^|\\s)(?:minecraft:)?whitelist(?:$|\\s)"
48
+ ]
49
+ };
50
+ const fullPermissions = {
51
+ preset: "full",
52
+ defaultMode: "allow",
53
+ allow: [],
54
+ deny: []
55
+ };
56
+ function presetPermissions(preset) {
57
+ if (preset === "readonly") return readonlyPermissions;
58
+ if (preset === "guarded") return guardedPermissions;
59
+ return fullPermissions;
60
+ }
61
+ function assertRconPermissionPreset(value) {
62
+ if (value === "readonly" || value === "guarded" || value === "full") return value;
63
+ throw new Error(`Invalid RCON permission preset: ${value}`);
64
+ }
65
+ function assertRconPermissionMode(value) {
66
+ if (value === "allow" || value === "deny") return value;
67
+ throw new Error(`Invalid RCON permission defaultMode: ${value}`);
68
+ }
69
+ function stringArray(value, field) {
70
+ if (value === void 0) return [];
71
+ if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) throw new Error(`RCON permissions ${field} must be a string array`);
72
+ return value;
73
+ }
74
+ function normalizeRconCommand(command) {
75
+ return command.trim().replace(/^\/+/, "").replace(/\s+/g, " ");
76
+ }
77
+ function compileRconRegex(pattern) {
78
+ try {
79
+ return new RegExp(pattern, "i");
80
+ } catch (error) {
81
+ throw new Error(`Invalid RCON permission regex ${JSON.stringify(pattern)}: ${error instanceof Error ? error.message : String(error)}`);
82
+ }
83
+ }
84
+ function resolveRconPermissions(permissions) {
85
+ if (!permissions) return {
86
+ ...readonlyPermissions,
87
+ allow: [...readonlyPermissions.allow],
88
+ deny: []
89
+ };
90
+ const preset = permissions.preset ? assertRconPermissionPreset(permissions.preset) : void 0;
91
+ const defaultMode = permissions.defaultMode ? assertRconPermissionMode(permissions.defaultMode) : void 0;
92
+ const allow = stringArray(permissions.allow, "allow");
93
+ const deny = stringArray(permissions.deny, "deny");
94
+ const base = permissions.preset ? presetPermissions(preset ?? "readonly") : {
95
+ preset: "readonly",
96
+ defaultMode: "deny",
97
+ allow: [],
98
+ deny: []
99
+ };
100
+ return {
101
+ preset: preset ?? base.preset,
102
+ defaultMode: defaultMode ?? base.defaultMode,
103
+ allow: [...base.allow, ...allow],
104
+ deny: [...base.deny, ...deny]
105
+ };
106
+ }
107
+ function evaluateRconPermission(command, permissions) {
108
+ const normalizedCommand = normalizeRconCommand(command);
109
+ const resolved = resolveRconPermissions(permissions);
110
+ for (const pattern of resolved.deny) if (compileRconRegex(pattern).test(normalizedCommand)) return {
111
+ allowed: false,
112
+ defaultMode: resolved.defaultMode,
113
+ matchedRule: {
114
+ source: "deny",
115
+ pattern
116
+ },
117
+ normalizedCommand
118
+ };
119
+ for (const pattern of resolved.allow) if (compileRconRegex(pattern).test(normalizedCommand)) return {
120
+ allowed: true,
121
+ defaultMode: resolved.defaultMode,
122
+ matchedRule: {
123
+ source: "allow",
124
+ pattern
125
+ },
126
+ normalizedCommand
127
+ };
128
+ return {
129
+ allowed: resolved.defaultMode === "allow",
130
+ defaultMode: resolved.defaultMode,
131
+ matchedRule: {
132
+ source: "default",
133
+ pattern: null
134
+ },
135
+ normalizedCommand
136
+ };
137
+ }
138
+ function configHome() {
139
+ if (process.platform === "darwin") return join(homedir(), "Library", "Application Support", "minecraft-skills");
140
+ if (process.platform === "win32") return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "minecraft-skills");
141
+ return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "minecraft-skills");
142
+ }
143
+ function defaultRconConfigPath() {
144
+ return join(configHome(), "rcon.json");
145
+ }
146
+ function localRconConfigCandidates(cwd = process.cwd()) {
147
+ return [join(cwd, ".minecraft-skills", "rcon.json"), join(cwd, "minecraft-skills.rcon.json")];
148
+ }
149
+ function findRconConfigPath(explicitPath) {
150
+ if (explicitPath) return {
151
+ path: resolve(explicitPath),
152
+ source: "explicit"
153
+ };
154
+ if (process.env.MINECRAFT_SKILLS_RCON_CONFIG) return {
155
+ path: resolve(process.env.MINECRAFT_SKILLS_RCON_CONFIG),
156
+ source: "env-config"
157
+ };
158
+ for (const candidate of localRconConfigCandidates()) if (existsSync(candidate)) return {
159
+ path: candidate,
160
+ source: "local"
161
+ };
162
+ const userPath = defaultRconConfigPath();
163
+ if (existsSync(userPath)) return {
164
+ path: userPath,
165
+ source: "user"
166
+ };
167
+ return {
168
+ path: null,
169
+ source: "missing"
170
+ };
171
+ }
172
+ function readRconConfig(path) {
173
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
174
+ if (typeof parsed !== "object" || parsed === null || !("profiles" in parsed)) throw new Error(`Invalid RCON config: ${path}`);
175
+ const config = parsed;
176
+ if (typeof config.profiles !== "object" || config.profiles === null) throw new Error(`Invalid RCON config profiles: ${path}`);
177
+ return config;
178
+ }
179
+ function envValue(name) {
180
+ const value = process.env[name];
181
+ return value && value.length > 0 ? value : void 0;
182
+ }
183
+ function resolveConfigValue(value, field, missing) {
184
+ if (typeof value === "number") return String(value);
185
+ if (value.startsWith("$env:")) {
186
+ const name = value.slice(5);
187
+ const resolved = envValue(name);
188
+ if (!resolved) {
189
+ missing.push(name);
190
+ return "";
191
+ }
192
+ return resolved;
193
+ }
194
+ if (field === "password") return value;
195
+ return value;
196
+ }
197
+ function parsePort(value, missing) {
198
+ const resolved = resolveConfigValue(value, "port", missing);
199
+ if (!resolved) return defaultRconPort;
200
+ const port = Number(resolved);
201
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) throw new Error(`Invalid RCON port: ${resolved}`);
202
+ return port;
203
+ }
204
+ function parseTimeoutMs(value, missing) {
205
+ const resolved = resolveConfigValue(value, "timeoutMs", missing);
206
+ if (!resolved) return defaultRconTimeoutMs;
207
+ const timeoutMs = Number(resolved);
208
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new Error(`Invalid RCON timeoutMs: ${resolved}`);
209
+ return timeoutMs;
210
+ }
211
+ function envProfile() {
212
+ const host = envValue("MINECRAFT_SKILLS_RCON_HOST");
213
+ const password = envValue("MINECRAFT_SKILLS_RCON_PASSWORD");
214
+ if (!host || !password) return null;
215
+ return {
216
+ host,
217
+ port: envValue("MINECRAFT_SKILLS_RCON_PORT") ?? defaultRconPort,
218
+ password,
219
+ timeoutMs: envValue("MINECRAFT_SKILLS_RCON_TIMEOUT_MS") ?? defaultRconTimeoutMs,
220
+ permissions: { preset: envValue("MINECRAFT_SKILLS_RCON_PRESET") ?? "readonly" }
221
+ };
222
+ }
223
+ function profileNameFrom(config, requested) {
224
+ return requested ?? envValue("MINECRAFT_SKILLS_RCON_PROFILE") ?? config?.defaultProfile ?? null;
225
+ }
226
+ function resolveRconProfile(options = {}) {
227
+ const missing = [];
228
+ const warnings = [];
229
+ const found = findRconConfigPath(options.configPath);
230
+ let config = null;
231
+ if (found.path) {
232
+ if (!existsSync(found.path)) return { status: {
233
+ configured: false,
234
+ configPath: found.path,
235
+ source: found.source,
236
+ profile: null,
237
+ missing: [found.path],
238
+ warnings,
239
+ message: `RCON config file does not exist: ${found.path}`
240
+ } };
241
+ config = readRconConfig(found.path);
242
+ }
243
+ const profileName = profileNameFrom(config, options.profile) ?? "env";
244
+ const profileConfig = config?.profiles[profileName] ?? (!config ? envProfile() : null);
245
+ if (!profileConfig) return { status: {
246
+ configured: false,
247
+ configPath: found.path,
248
+ source: found.source,
249
+ profile: profileName,
250
+ missing: config ? [`profiles.${profileName}`] : ["MINECRAFT_SKILLS_RCON_HOST", "MINECRAFT_SKILLS_RCON_PASSWORD"],
251
+ warnings,
252
+ message: config ? `RCON profile is not defined: ${profileName}` : "Set an RCON config file or MINECRAFT_SKILLS_RCON_HOST and MINECRAFT_SKILLS_RCON_PASSWORD."
253
+ } };
254
+ const merged = {
255
+ ...profileConfig,
256
+ ...envValue("MINECRAFT_SKILLS_RCON_HOST") ? { host: "$env:MINECRAFT_SKILLS_RCON_HOST" } : {},
257
+ ...envValue("MINECRAFT_SKILLS_RCON_PORT") ? { port: "$env:MINECRAFT_SKILLS_RCON_PORT" } : {},
258
+ ...envValue("MINECRAFT_SKILLS_RCON_PASSWORD") ? { password: "$env:MINECRAFT_SKILLS_RCON_PASSWORD" } : {},
259
+ ...envValue("MINECRAFT_SKILLS_RCON_TIMEOUT_MS") ? { timeoutMs: "$env:MINECRAFT_SKILLS_RCON_TIMEOUT_MS" } : {}
260
+ };
261
+ const host = resolveConfigValue(merged.host, "host", missing);
262
+ const password = resolveConfigValue(merged.password, "password", missing);
263
+ const port = parsePort(merged.port ?? defaultRconPort, missing);
264
+ const timeoutMs = parseTimeoutMs(merged.timeoutMs ?? defaultRconTimeoutMs, missing);
265
+ if (typeof merged.password === "string" && !merged.password.startsWith("$env:")) warnings.push("RCON password is stored as a literal value. Prefer password: \"$env:NAME\".");
266
+ if (missing.length > 0 || !host || !password) return { status: {
267
+ configured: false,
268
+ configPath: found.path,
269
+ source: found.path ? found.source : "missing",
270
+ profile: profileName,
271
+ missing,
272
+ warnings,
273
+ message: `RCON profile ${profileName} is missing required values.`
274
+ } };
275
+ const envPreset = envValue("MINECRAFT_SKILLS_RCON_PRESET");
276
+ const permissions = envPreset ? {
277
+ ...merged.permissions ?? {},
278
+ preset: envPreset
279
+ } : merged.permissions;
280
+ const profile = {
281
+ configPath: found.path,
282
+ source: found.path ? found.source : "env-profile",
283
+ profile: profileName,
284
+ host,
285
+ port,
286
+ password,
287
+ timeoutMs,
288
+ permissions: resolveRconPermissions(permissions),
289
+ warnings
290
+ };
291
+ return {
292
+ profile,
293
+ status: {
294
+ configured: true,
295
+ configPath: found.path,
296
+ source: profile.source,
297
+ profile: profileName,
298
+ missing: [],
299
+ warnings,
300
+ message: `RCON profile ${profileName} is configured.`
301
+ }
302
+ };
303
+ }
304
+ function getRconConfigStatus(options = {}) {
305
+ return resolveRconProfile(options).status;
306
+ }
307
+ function isRconConfigured(options = {}) {
308
+ return getRconConfigStatus(options).configured;
309
+ }
310
+ function createRconConfig(options = {}) {
311
+ const path = resolve(options.configPath ?? defaultRconConfigPath());
312
+ const existed = existsSync(path);
313
+ const profile = options.profile ?? "local";
314
+ const config = {
315
+ $schema: "https://raw.githubusercontent.com/sya-ri/minecraft-skills/main/schema/rcon-config.schema.json",
316
+ defaultProfile: profile,
317
+ profiles: { [profile]: {
318
+ host: options.host ?? "127.0.0.1",
319
+ port: options.port ?? defaultRconPort,
320
+ password: `$env:${options.passwordEnv ?? "MINECRAFT_SKILLS_RCON_PASSWORD"}`,
321
+ permissions: { preset: options.preset ?? "readonly" }
322
+ } }
323
+ };
324
+ if (existed && !options.force) return {
325
+ path,
326
+ existed,
327
+ written: false,
328
+ warning: `RCON config already exists and was not overwritten: ${path}`,
329
+ config
330
+ };
331
+ mkdirSync(dirname(path), { recursive: true });
332
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
333
+ return {
334
+ path,
335
+ existed,
336
+ written: true,
337
+ warning: existed ? `Overwrote existing RCON config: ${path}` : null,
338
+ config
339
+ };
340
+ }
341
+ function writePacket(socket, id, type, payload) {
342
+ const bodyLength = Buffer.byteLength(payload) + 10;
343
+ const buffer = Buffer.alloc(bodyLength + 4);
344
+ buffer.writeInt32LE(bodyLength, 0);
345
+ buffer.writeInt32LE(id, 4);
346
+ buffer.writeInt32LE(type, 8);
347
+ buffer.write(payload, 12, "utf8");
348
+ buffer.writeInt16LE(0, bodyLength + 2);
349
+ socket.write(buffer);
350
+ }
351
+ function parsePackets(buffer) {
352
+ const packets = [];
353
+ let offset = 0;
354
+ while (buffer.length - offset >= 4) {
355
+ const length = buffer.readInt32LE(offset);
356
+ if (buffer.length - offset < length + 4) break;
357
+ const start = offset + 4;
358
+ const id = buffer.readInt32LE(start);
359
+ const type = buffer.readInt32LE(start + 4);
360
+ const body = buffer.subarray(start + 8, start + length - 2).toString("utf8");
361
+ packets.push({
362
+ id,
363
+ type,
364
+ body
365
+ });
366
+ offset += length + 4;
367
+ }
368
+ return {
369
+ packets,
370
+ rest: buffer.subarray(offset)
371
+ };
372
+ }
373
+ async function readPacketsUntil(socket, timeoutMs, stop) {
374
+ return new Promise((resolvePromise, reject) => {
375
+ let pending = Buffer.alloc(0);
376
+ const packets = [];
377
+ const timer = setTimeout(() => {
378
+ cleanup();
379
+ reject(/* @__PURE__ */ new Error("RCON response timed out"));
380
+ }, timeoutMs);
381
+ const cleanup = () => {
382
+ clearTimeout(timer);
383
+ socket.off("data", onData);
384
+ socket.off("error", onError);
385
+ socket.off("end", onEnd);
386
+ socket.off("close", onClose);
387
+ socket.off("timeout", onTimeout);
388
+ };
389
+ const onError = (error) => {
390
+ cleanup();
391
+ reject(error);
392
+ };
393
+ const onEnd = () => {
394
+ cleanup();
395
+ reject(/* @__PURE__ */ new Error("RCON connection ended before the expected response was received"));
396
+ };
397
+ const onClose = () => {
398
+ cleanup();
399
+ reject(/* @__PURE__ */ new Error("RCON connection closed before the expected response was received"));
400
+ };
401
+ const onTimeout = () => {
402
+ cleanup();
403
+ socket.destroy();
404
+ reject(/* @__PURE__ */ new Error("RCON response timed out"));
405
+ };
406
+ const onData = (chunk) => {
407
+ pending = Buffer.concat([pending, chunk]);
408
+ const parsed = parsePackets(pending);
409
+ pending = parsed.rest;
410
+ for (const packet of parsed.packets) {
411
+ packets.push(packet);
412
+ if (stop(packet)) {
413
+ cleanup();
414
+ resolvePromise(packets);
415
+ return;
416
+ }
417
+ }
418
+ };
419
+ socket.on("data", onData);
420
+ socket.on("error", onError);
421
+ socket.on("end", onEnd);
422
+ socket.on("close", onClose);
423
+ socket.on("timeout", onTimeout);
424
+ });
425
+ }
426
+ async function sendRconCommand(profile, command) {
427
+ const socket = new Socket();
428
+ socket.setTimeout(profile.timeoutMs);
429
+ await new Promise((resolvePromise, reject) => {
430
+ const timer = setTimeout(() => {
431
+ cleanup();
432
+ socket.destroy();
433
+ reject(/* @__PURE__ */ new Error("RCON connection timed out"));
434
+ }, profile.timeoutMs);
435
+ const cleanup = () => {
436
+ clearTimeout(timer);
437
+ socket.off("error", onError);
438
+ };
439
+ const onError = (error) => {
440
+ cleanup();
441
+ reject(error);
442
+ };
443
+ socket.once("error", onError);
444
+ socket.connect(profile.port, profile.host, () => {
445
+ cleanup();
446
+ resolvePromise();
447
+ });
448
+ });
449
+ try {
450
+ const authPromise = readPacketsUntil(socket, profile.timeoutMs, (packet) => packet.id === 1 || packet.id === -1);
451
+ writePacket(socket, 1, 3, profile.password);
452
+ if ((await authPromise).some((packet) => packet.id === -1)) throw new Error("RCON authentication failed");
453
+ const responsePromise = readPacketsUntil(socket, profile.timeoutMs, (packet) => packet.id === 3);
454
+ writePacket(socket, 2, 2, command);
455
+ writePacket(socket, 3, 2, "");
456
+ return (await responsePromise).filter((packet) => packet.id === 2).map((packet) => packet.body).join("");
457
+ } finally {
458
+ socket.end();
459
+ }
460
+ }
461
+ async function runRconCommand(options, transport = sendRconCommand) {
462
+ const resolved = resolveRconProfile(options);
463
+ if (!resolved.profile) throw new Error(resolved.status.message);
464
+ const command = normalizeRconCommand(options.command);
465
+ if (!command) throw new Error("RCON command must not be empty");
466
+ const permissionDecision = evaluateRconPermission(command, resolved.profile.permissions);
467
+ if (!permissionDecision.allowed) return {
468
+ profile: resolved.profile.profile,
469
+ host: resolved.profile.host,
470
+ port: resolved.profile.port,
471
+ command,
472
+ response: "",
473
+ permissionDecision
474
+ };
475
+ const profile = options.timeoutMs ? {
476
+ ...resolved.profile,
477
+ timeoutMs: options.timeoutMs
478
+ } : resolved.profile;
479
+ return {
480
+ profile: profile.profile,
481
+ host: profile.host,
482
+ port: profile.port,
483
+ command,
484
+ response: await transport(profile, command),
485
+ permissionDecision
486
+ };
487
+ }
488
+ //#endregion
489
+ export { createRconConfig, defaultRconConfigPath, evaluateRconPermission, getRconConfigStatus, isRconConfigured, resolveRconPermissions, runRconCommand, sendRconCommand };
490
+
491
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { Socket } from \"node:net\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nexport type RconPermissionPreset = \"readonly\" | \"guarded\" | \"full\";\nexport type RconPermissionMode = \"allow\" | \"deny\";\n\nexport type RconPermissions = {\n preset?: RconPermissionPreset;\n defaultMode?: RconPermissionMode;\n allow?: string[];\n deny?: string[];\n};\n\nexport type RconProfileConfig = {\n host: string;\n port: number | string;\n password: string;\n timeoutMs?: number | string;\n permissions?: RconPermissions;\n};\n\nexport type RconConfig = {\n $schema?: string;\n defaultProfile?: string;\n profiles: Record<string, RconProfileConfig>;\n};\n\nexport type RconConfigStatusOptions = {\n configPath?: string;\n profile?: string;\n};\n\nexport type RconConfigStatus = {\n configured: boolean;\n configPath: string | null;\n source: \"explicit\" | \"env-config\" | \"local\" | \"user\" | \"env-profile\" | \"missing\";\n profile: string | null;\n missing: string[];\n warnings: string[];\n message: string;\n};\n\nexport type RconCreateConfigOptions = {\n configPath?: string;\n profile?: string;\n host?: string;\n port?: number | string;\n passwordEnv?: string;\n preset?: RconPermissionPreset;\n force?: boolean;\n};\n\nexport type RconCreateConfigResult = {\n path: string;\n existed: boolean;\n written: boolean;\n warning: string | null;\n config: RconConfig;\n};\n\nexport type RconCommandOptions = {\n command: string;\n configPath?: string;\n profile?: string;\n timeoutMs?: number;\n};\n\nexport type RconPermissionDecision = {\n allowed: boolean;\n defaultMode: RconPermissionMode;\n matchedRule: {\n source: \"allow\" | \"deny\" | \"default\";\n pattern: string | null;\n };\n normalizedCommand: string;\n};\n\nexport type RconCommandResult = {\n profile: string;\n host: string;\n port: number;\n command: string;\n response: string;\n permissionDecision: RconPermissionDecision;\n};\n\nexport type ResolvedRconProfile = {\n configPath: string | null;\n source: RconConfigStatus[\"source\"];\n profile: string;\n host: string;\n port: number;\n password: string;\n timeoutMs: number;\n permissions: Required<RconPermissions>;\n warnings: string[];\n};\n\nexport type RconTransport = (profile: ResolvedRconProfile, command: string) => Promise<string>;\n\nconst defaultRconPort = 25575;\nconst defaultRconTimeoutMs = 2000;\n\nconst readonlyPermissions: Required<RconPermissions> = {\n preset: \"readonly\",\n defaultMode: \"deny\",\n allow: [\n \"^list$\",\n \"^help(?: .+)?$\",\n \"^minecraft:help(?: .+)?$\",\n \"^version$\",\n \"^plugins$\",\n \"^pl$\",\n \"^bukkit:version$\",\n \"^bukkit:plugins$\",\n \"^time query (?:daytime|gametime|day)$\",\n \"^difficulty$\",\n \"^worldborder get$\",\n \"^gamerule [A-Za-z0-9_.:-]+$\",\n \"^datapack list(?: .+)?$\",\n \"^team list(?: .+)?$\",\n \"^scoreboard objectives list$\",\n \"^scoreboard players list(?: .+)?$\",\n \"^scoreboard players get .+$\",\n \"^bossbar list$\",\n \"^bossbar get .+$\",\n \"^attribute .+ get .+$\",\n \"^(?:experience|xp) query .+$\",\n \"^data get .+$\",\n \"^tag .+ list$\",\n \"^forceload query(?: .+)?$\",\n ],\n deny: [],\n};\n\nconst guardedPermissions: Required<RconPermissions> = {\n preset: \"guarded\",\n defaultMode: \"allow\",\n allow: [],\n deny: [\n \"(?:^|\\\\s)(?:minecraft:)?(?:stop|restart)(?:$|\\\\s)\",\n \"(?:^|\\\\s)(?:minecraft:)?save-[A-Za-z-]+(?:$|\\\\s)\",\n \"(?:^|\\\\s)(?:minecraft:)?(?:op|deop|ban|ban-ip|pardon|pardon-ip|kick)(?:$|\\\\s)\",\n \"(?:^|\\\\s)(?:minecraft:)?whitelist(?:$|\\\\s)\",\n ],\n};\n\nconst fullPermissions: Required<RconPermissions> = {\n preset: \"full\",\n defaultMode: \"allow\",\n allow: [],\n deny: [],\n};\n\nfunction presetPermissions(preset: RconPermissionPreset): Required<RconPermissions> {\n if (preset === \"readonly\") {\n return readonlyPermissions;\n }\n if (preset === \"guarded\") {\n return guardedPermissions;\n }\n return fullPermissions;\n}\n\nfunction assertRconPermissionPreset(value: string): RconPermissionPreset {\n if (value === \"readonly\" || value === \"guarded\" || value === \"full\") {\n return value;\n }\n throw new Error(`Invalid RCON permission preset: ${value}`);\n}\n\nfunction assertRconPermissionMode(value: string): RconPermissionMode {\n if (value === \"allow\" || value === \"deny\") {\n return value;\n }\n throw new Error(`Invalid RCON permission defaultMode: ${value}`);\n}\n\nfunction stringArray(value: unknown, field: string): string[] {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value) || !value.every((entry) => typeof entry === \"string\")) {\n throw new Error(`RCON permissions ${field} must be a string array`);\n }\n return value;\n}\n\nfunction normalizeRconCommand(command: string): string {\n return command.trim().replace(/^\\/+/, \"\").replace(/\\s+/g, \" \");\n}\n\nfunction compileRconRegex(pattern: string): RegExp {\n try {\n return new RegExp(pattern, \"i\");\n } catch (error) {\n throw new Error(\n `Invalid RCON permission regex ${JSON.stringify(pattern)}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n}\n\nexport function resolveRconPermissions(permissions?: RconPermissions): Required<RconPermissions> {\n if (!permissions) {\n return { ...readonlyPermissions, allow: [...readonlyPermissions.allow], deny: [] };\n }\n const preset = permissions.preset ? assertRconPermissionPreset(permissions.preset) : undefined;\n const defaultMode = permissions.defaultMode\n ? assertRconPermissionMode(permissions.defaultMode)\n : undefined;\n const allow = stringArray(permissions.allow, \"allow\");\n const deny = stringArray(permissions.deny, \"deny\");\n const base = permissions.preset\n ? presetPermissions(preset ?? \"readonly\")\n : {\n preset: \"readonly\" as const,\n defaultMode: \"deny\" as const,\n allow: [],\n deny: [],\n };\n return {\n preset: preset ?? base.preset,\n defaultMode: defaultMode ?? base.defaultMode,\n allow: [...base.allow, ...allow],\n deny: [...base.deny, ...deny],\n };\n}\n\nexport function evaluateRconPermission(\n command: string,\n permissions?: RconPermissions,\n): RconPermissionDecision {\n const normalizedCommand = normalizeRconCommand(command);\n const resolved = resolveRconPermissions(permissions);\n for (const pattern of resolved.deny) {\n if (compileRconRegex(pattern).test(normalizedCommand)) {\n return {\n allowed: false,\n defaultMode: resolved.defaultMode,\n matchedRule: { source: \"deny\", pattern },\n normalizedCommand,\n };\n }\n }\n for (const pattern of resolved.allow) {\n if (compileRconRegex(pattern).test(normalizedCommand)) {\n return {\n allowed: true,\n defaultMode: resolved.defaultMode,\n matchedRule: { source: \"allow\", pattern },\n normalizedCommand,\n };\n }\n }\n return {\n allowed: resolved.defaultMode === \"allow\",\n defaultMode: resolved.defaultMode,\n matchedRule: { source: \"default\", pattern: null },\n normalizedCommand,\n };\n}\n\nfunction configHome(): string {\n if (process.platform === \"darwin\") {\n return join(homedir(), \"Library\", \"Application Support\", \"minecraft-skills\");\n }\n if (process.platform === \"win32\") {\n return join(process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\"), \"minecraft-skills\");\n }\n return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\"), \"minecraft-skills\");\n}\n\nexport function defaultRconConfigPath(): string {\n return join(configHome(), \"rcon.json\");\n}\n\nfunction localRconConfigCandidates(cwd = process.cwd()): string[] {\n return [join(cwd, \".minecraft-skills\", \"rcon.json\"), join(cwd, \"minecraft-skills.rcon.json\")];\n}\n\nfunction findRconConfigPath(explicitPath?: string): {\n path: string | null;\n source: RconConfigStatus[\"source\"];\n} {\n if (explicitPath) {\n return { path: resolve(explicitPath), source: \"explicit\" };\n }\n if (process.env.MINECRAFT_SKILLS_RCON_CONFIG) {\n return { path: resolve(process.env.MINECRAFT_SKILLS_RCON_CONFIG), source: \"env-config\" };\n }\n for (const candidate of localRconConfigCandidates()) {\n if (existsSync(candidate)) {\n return { path: candidate, source: \"local\" };\n }\n }\n const userPath = defaultRconConfigPath();\n if (existsSync(userPath)) {\n return { path: userPath, source: \"user\" };\n }\n return { path: null, source: \"missing\" };\n}\n\nfunction readRconConfig(path: string): RconConfig {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n if (typeof parsed !== \"object\" || parsed === null || !(\"profiles\" in parsed)) {\n throw new Error(`Invalid RCON config: ${path}`);\n }\n const config = parsed as RconConfig;\n if (typeof config.profiles !== \"object\" || config.profiles === null) {\n throw new Error(`Invalid RCON config profiles: ${path}`);\n }\n return config;\n}\n\nfunction envValue(name: string): string | undefined {\n const value = process.env[name];\n return value && value.length > 0 ? value : undefined;\n}\n\nfunction resolveConfigValue(value: string | number, field: string, missing: string[]): string {\n if (typeof value === \"number\") {\n return String(value);\n }\n if (value.startsWith(\"$env:\")) {\n const name = value.slice(\"$env:\".length);\n const resolved = envValue(name);\n if (!resolved) {\n missing.push(name);\n return \"\";\n }\n return resolved;\n }\n if (field === \"password\") {\n return value;\n }\n return value;\n}\n\nfunction parsePort(value: string | number, missing: string[]): number {\n const resolved = resolveConfigValue(value, \"port\", missing);\n if (!resolved) {\n return defaultRconPort;\n }\n const port = Number(resolved);\n if (!Number.isInteger(port) || port <= 0 || port > 65535) {\n throw new Error(`Invalid RCON port: ${resolved}`);\n }\n return port;\n}\n\nfunction parseTimeoutMs(value: string | number, missing: string[]): number {\n const resolved = resolveConfigValue(value, \"timeoutMs\", missing);\n if (!resolved) {\n return defaultRconTimeoutMs;\n }\n const timeoutMs = Number(resolved);\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n throw new Error(`Invalid RCON timeoutMs: ${resolved}`);\n }\n return timeoutMs;\n}\n\nfunction envProfile(): RconProfileConfig | null {\n const host = envValue(\"MINECRAFT_SKILLS_RCON_HOST\");\n const password = envValue(\"MINECRAFT_SKILLS_RCON_PASSWORD\");\n if (!host || !password) {\n return null;\n }\n return {\n host,\n port: envValue(\"MINECRAFT_SKILLS_RCON_PORT\") ?? defaultRconPort,\n password,\n timeoutMs: envValue(\"MINECRAFT_SKILLS_RCON_TIMEOUT_MS\") ?? defaultRconTimeoutMs,\n permissions: {\n preset:\n (envValue(\"MINECRAFT_SKILLS_RCON_PRESET\") as RconPermissionPreset | undefined) ??\n \"readonly\",\n },\n };\n}\n\nfunction profileNameFrom(config: RconConfig | null, requested?: string): string | null {\n return requested ?? envValue(\"MINECRAFT_SKILLS_RCON_PROFILE\") ?? config?.defaultProfile ?? null;\n}\n\nfunction resolveRconProfile(options: RconConfigStatusOptions = {}): {\n profile?: ResolvedRconProfile;\n status: RconConfigStatus;\n} {\n const missing: string[] = [];\n const warnings: string[] = [];\n const found = findRconConfigPath(options.configPath);\n let config: RconConfig | null = null;\n if (found.path) {\n if (!existsSync(found.path)) {\n return {\n status: {\n configured: false,\n configPath: found.path,\n source: found.source,\n profile: null,\n missing: [found.path],\n warnings,\n message: `RCON config file does not exist: ${found.path}`,\n },\n };\n }\n config = readRconConfig(found.path);\n }\n\n const profileName = profileNameFrom(config, options.profile) ?? \"env\";\n const configProfile = config?.profiles[profileName];\n const profileConfig = configProfile ?? (!config ? envProfile() : null);\n if (!profileConfig) {\n return {\n status: {\n configured: false,\n configPath: found.path,\n source: found.source,\n profile: profileName,\n missing: config\n ? [`profiles.${profileName}`]\n : [\"MINECRAFT_SKILLS_RCON_HOST\", \"MINECRAFT_SKILLS_RCON_PASSWORD\"],\n warnings,\n message: config\n ? `RCON profile is not defined: ${profileName}`\n : \"Set an RCON config file or MINECRAFT_SKILLS_RCON_HOST and MINECRAFT_SKILLS_RCON_PASSWORD.\",\n },\n };\n }\n\n const merged: RconProfileConfig = {\n ...profileConfig,\n ...(envValue(\"MINECRAFT_SKILLS_RCON_HOST\") ? { host: \"$env:MINECRAFT_SKILLS_RCON_HOST\" } : {}),\n ...(envValue(\"MINECRAFT_SKILLS_RCON_PORT\") ? { port: \"$env:MINECRAFT_SKILLS_RCON_PORT\" } : {}),\n ...(envValue(\"MINECRAFT_SKILLS_RCON_PASSWORD\")\n ? { password: \"$env:MINECRAFT_SKILLS_RCON_PASSWORD\" }\n : {}),\n ...(envValue(\"MINECRAFT_SKILLS_RCON_TIMEOUT_MS\")\n ? { timeoutMs: \"$env:MINECRAFT_SKILLS_RCON_TIMEOUT_MS\" }\n : {}),\n };\n const host = resolveConfigValue(merged.host, \"host\", missing);\n const password = resolveConfigValue(merged.password, \"password\", missing);\n const port = parsePort(merged.port ?? defaultRconPort, missing);\n const timeoutMs = parseTimeoutMs(merged.timeoutMs ?? defaultRconTimeoutMs, missing);\n if (typeof merged.password === \"string\" && !merged.password.startsWith(\"$env:\")) {\n warnings.push('RCON password is stored as a literal value. Prefer password: \"$env:NAME\".');\n }\n if (missing.length > 0 || !host || !password) {\n return {\n status: {\n configured: false,\n configPath: found.path,\n source: found.path ? found.source : \"missing\",\n profile: profileName,\n missing,\n warnings,\n message: `RCON profile ${profileName} is missing required values.`,\n },\n };\n }\n const envPreset = envValue(\"MINECRAFT_SKILLS_RCON_PRESET\");\n const permissions = envPreset\n ? {\n ...(merged.permissions ?? {}),\n preset: envPreset as RconPermissionPreset,\n }\n : merged.permissions;\n const profile: ResolvedRconProfile = {\n configPath: found.path,\n source: found.path ? found.source : \"env-profile\",\n profile: profileName,\n host,\n port,\n password,\n timeoutMs,\n permissions: resolveRconPermissions(permissions),\n warnings,\n };\n return {\n profile,\n status: {\n configured: true,\n configPath: found.path,\n source: profile.source,\n profile: profileName,\n missing: [],\n warnings,\n message: `RCON profile ${profileName} is configured.`,\n },\n };\n}\n\nexport function getRconConfigStatus(options: RconConfigStatusOptions = {}): RconConfigStatus {\n return resolveRconProfile(options).status;\n}\n\nexport function isRconConfigured(options: RconConfigStatusOptions = {}): boolean {\n return getRconConfigStatus(options).configured;\n}\n\nexport function createRconConfig(options: RconCreateConfigOptions = {}): RconCreateConfigResult {\n const path = resolve(options.configPath ?? defaultRconConfigPath());\n const existed = existsSync(path);\n const profile = options.profile ?? \"local\";\n const config: RconConfig = {\n $schema:\n \"https://raw.githubusercontent.com/sya-ri/minecraft-skills/main/schema/rcon-config.schema.json\",\n defaultProfile: profile,\n profiles: {\n [profile]: {\n host: options.host ?? \"127.0.0.1\",\n port: options.port ?? defaultRconPort,\n password: `$env:${options.passwordEnv ?? \"MINECRAFT_SKILLS_RCON_PASSWORD\"}`,\n permissions: {\n preset: options.preset ?? \"readonly\",\n },\n },\n },\n };\n if (existed && !options.force) {\n return {\n path,\n existed,\n written: false,\n warning: `RCON config already exists and was not overwritten: ${path}`,\n config,\n };\n }\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n return {\n path,\n existed,\n written: true,\n warning: existed ? `Overwrote existing RCON config: ${path}` : null,\n config,\n };\n}\n\nfunction writePacket(socket: Socket, id: number, type: number, payload: string): void {\n const bodyLength = Buffer.byteLength(payload) + 10;\n const buffer = Buffer.alloc(bodyLength + 4);\n buffer.writeInt32LE(bodyLength, 0);\n buffer.writeInt32LE(id, 4);\n buffer.writeInt32LE(type, 8);\n buffer.write(payload, 12, \"utf8\");\n buffer.writeInt16LE(0, bodyLength + 2);\n socket.write(buffer);\n}\n\nfunction parsePackets(buffer: Buffer<ArrayBufferLike>): {\n packets: Array<{ id: number; type: number; body: string }>;\n rest: Buffer<ArrayBufferLike>;\n} {\n const packets: Array<{ id: number; type: number; body: string }> = [];\n let offset = 0;\n while (buffer.length - offset >= 4) {\n const length = buffer.readInt32LE(offset);\n if (buffer.length - offset < length + 4) {\n break;\n }\n const start = offset + 4;\n const id = buffer.readInt32LE(start);\n const type = buffer.readInt32LE(start + 4);\n const body = buffer.subarray(start + 8, start + length - 2).toString(\"utf8\");\n packets.push({ id, type, body });\n offset += length + 4;\n }\n return { packets, rest: buffer.subarray(offset) };\n}\n\nasync function readPacketsUntil(\n socket: Socket,\n timeoutMs: number,\n stop: (packet: { id: number; type: number; body: string }) => boolean,\n): Promise<Array<{ id: number; type: number; body: string }>> {\n return new Promise((resolvePromise, reject) => {\n let pending: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n const packets: Array<{ id: number; type: number; body: string }> = [];\n const timer = setTimeout(() => {\n cleanup();\n reject(new Error(\"RCON response timed out\"));\n }, timeoutMs);\n const cleanup = () => {\n clearTimeout(timer);\n socket.off(\"data\", onData);\n socket.off(\"error\", onError);\n socket.off(\"end\", onEnd);\n socket.off(\"close\", onClose);\n socket.off(\"timeout\", onTimeout);\n };\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n const onEnd = () => {\n cleanup();\n reject(new Error(\"RCON connection ended before the expected response was received\"));\n };\n const onClose = () => {\n cleanup();\n reject(new Error(\"RCON connection closed before the expected response was received\"));\n };\n const onTimeout = () => {\n cleanup();\n socket.destroy();\n reject(new Error(\"RCON response timed out\"));\n };\n const onData = (chunk: Buffer) => {\n pending = Buffer.concat([pending, chunk]);\n const parsed = parsePackets(pending);\n pending = parsed.rest;\n for (const packet of parsed.packets) {\n packets.push(packet);\n if (stop(packet)) {\n cleanup();\n resolvePromise(packets);\n return;\n }\n }\n };\n socket.on(\"data\", onData);\n socket.on(\"error\", onError);\n socket.on(\"end\", onEnd);\n socket.on(\"close\", onClose);\n socket.on(\"timeout\", onTimeout);\n });\n}\n\nexport async function sendRconCommand(\n profile: ResolvedRconProfile,\n command: string,\n): Promise<string> {\n const socket = new Socket();\n socket.setTimeout(profile.timeoutMs);\n await new Promise<void>((resolvePromise, reject) => {\n const timer = setTimeout(() => {\n cleanup();\n socket.destroy();\n reject(new Error(\"RCON connection timed out\"));\n }, profile.timeoutMs);\n const cleanup = () => {\n clearTimeout(timer);\n socket.off(\"error\", onError);\n };\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n socket.once(\"error\", onError);\n socket.connect(profile.port, profile.host, () => {\n cleanup();\n resolvePromise();\n });\n });\n try {\n const authPromise = readPacketsUntil(\n socket,\n profile.timeoutMs,\n (packet) => packet.id === 1 || packet.id === -1,\n );\n writePacket(socket, 1, 3, profile.password);\n const authPackets = await authPromise;\n if (authPackets.some((packet) => packet.id === -1)) {\n throw new Error(\"RCON authentication failed\");\n }\n\n const responsePromise = readPacketsUntil(\n socket,\n profile.timeoutMs,\n (packet) => packet.id === 3,\n );\n writePacket(socket, 2, 2, command);\n writePacket(socket, 3, 2, \"\");\n const responsePackets = await responsePromise;\n return responsePackets\n .filter((packet) => packet.id === 2)\n .map((packet) => packet.body)\n .join(\"\");\n } finally {\n socket.end();\n }\n}\n\nexport async function runRconCommand(\n options: RconCommandOptions,\n transport: RconTransport = sendRconCommand,\n): Promise<RconCommandResult> {\n const resolved = resolveRconProfile(options);\n if (!resolved.profile) {\n throw new Error(resolved.status.message);\n }\n const command = normalizeRconCommand(options.command);\n if (!command) {\n throw new Error(\"RCON command must not be empty\");\n }\n const permissionDecision = evaluateRconPermission(command, resolved.profile.permissions);\n if (!permissionDecision.allowed) {\n return {\n profile: resolved.profile.profile,\n host: resolved.profile.host,\n port: resolved.profile.port,\n command,\n response: \"\",\n permissionDecision,\n };\n }\n const profile = options.timeoutMs\n ? { ...resolved.profile, timeoutMs: options.timeoutMs }\n : resolved.profile;\n return {\n profile: profile.profile,\n host: profile.host,\n port: profile.port,\n command,\n response: await transport(profile, command),\n permissionDecision,\n };\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@minecraft-skills/rcon",
3
+ "version": "0.1.3",
4
+ "type": "module",
5
+ "description": "RCON configuration, permissions, and execution utilities for Minecraft Skills.",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "minecraft",
9
+ "rcon",
10
+ "ai"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/sya-ri/minecraft-skills.git",
15
+ "directory": "packages/rcon"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/sya-ri/minecraft-skills/issues"
19
+ },
20
+ "homepage": "https://github.com/sya-ri/minecraft-skills#readme",
21
+ "engines": {
22
+ "node": ">=22.12"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.mts",
30
+ "default": "./dist/index.mjs"
31
+ }
32
+ },
33
+ "types": "./dist/index.d.mts",
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "devDependencies": {
38
+ "@types/node": "26.0.0",
39
+ "@typescript/native-preview": "7.0.0-dev.20260621.1",
40
+ "tsdown": "0.22.3",
41
+ "typescript": "6.0.3",
42
+ "vitest": "4.1.9"
43
+ },
44
+ "scripts": {
45
+ "build": "tsdown",
46
+ "typecheck": "tsgo -p tsconfig.json --noEmit",
47
+ "typecheck:tsc": "tsc -p tsconfig.json --noEmit",
48
+ "test": "vitest run"
49
+ }
50
+ }