@chrrxs/robloxstudio-mcp-inspector 2.19.1 → 2.20.0
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/dist/index.js
CHANGED
|
@@ -196,6 +196,9 @@ var init_bridge_service = __esm({
|
|
|
196
196
|
}
|
|
197
197
|
return removed;
|
|
198
198
|
}
|
|
199
|
+
async unregisterInstanceIdEverywhere(instanceId) {
|
|
200
|
+
return this.unregisterInstanceId(instanceId);
|
|
201
|
+
}
|
|
199
202
|
getInstances() {
|
|
200
203
|
return Array.from(this.instances.values());
|
|
201
204
|
}
|
|
@@ -451,23 +454,202 @@ var init_bridge_service = __esm({
|
|
|
451
454
|
}
|
|
452
455
|
});
|
|
453
456
|
|
|
457
|
+
// ../core/dist/roblox-docs.js
|
|
458
|
+
function isDocCategory(value) {
|
|
459
|
+
return DOC_CATEGORIES.includes(value);
|
|
460
|
+
}
|
|
461
|
+
function cacheGet(key) {
|
|
462
|
+
const entry = cache.get(key);
|
|
463
|
+
if (!entry)
|
|
464
|
+
return void 0;
|
|
465
|
+
const ttl = entry.notFound ? NEGATIVE_CACHE_TTL_MS : CACHE_TTL_MS;
|
|
466
|
+
if (Date.now() - entry.fetchedAt > ttl) {
|
|
467
|
+
cache.delete(key);
|
|
468
|
+
return void 0;
|
|
469
|
+
}
|
|
470
|
+
return entry;
|
|
471
|
+
}
|
|
472
|
+
function cacheSet(key, entry) {
|
|
473
|
+
if (cache.size >= MAX_CACHE_ENTRIES) {
|
|
474
|
+
const oldest = cache.keys().next().value;
|
|
475
|
+
if (oldest !== void 0)
|
|
476
|
+
cache.delete(oldest);
|
|
477
|
+
}
|
|
478
|
+
cache.set(key, entry);
|
|
479
|
+
}
|
|
480
|
+
function docUrl(category, name) {
|
|
481
|
+
return `${DOCS_BASE_URL}/${category}/${encodeURIComponent(name)}.md`;
|
|
482
|
+
}
|
|
483
|
+
async function fetchRobloxDoc(category, name) {
|
|
484
|
+
const key = `${category}/${name}`;
|
|
485
|
+
const cached = cacheGet(key);
|
|
486
|
+
if (cached) {
|
|
487
|
+
if (cached.notFound)
|
|
488
|
+
throw new DocNotFoundError(category, name);
|
|
489
|
+
return cached.content;
|
|
490
|
+
}
|
|
491
|
+
const controller = new AbortController();
|
|
492
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
493
|
+
let response;
|
|
494
|
+
try {
|
|
495
|
+
response = await fetch(docUrl(category, name), {
|
|
496
|
+
signal: controller.signal,
|
|
497
|
+
headers: { Accept: "text/markdown, text/plain" }
|
|
498
|
+
});
|
|
499
|
+
} catch (error) {
|
|
500
|
+
throw new Error(`Failed to fetch Roblox docs for ${key}: ${error instanceof Error ? error.message : String(error)}`);
|
|
501
|
+
} finally {
|
|
502
|
+
clearTimeout(timer);
|
|
503
|
+
}
|
|
504
|
+
if (response.status === 404) {
|
|
505
|
+
cacheSet(key, { fetchedAt: Date.now(), notFound: true });
|
|
506
|
+
throw new DocNotFoundError(category, name);
|
|
507
|
+
}
|
|
508
|
+
if (!response.ok) {
|
|
509
|
+
throw new Error(`Failed to fetch Roblox docs for ${key}: HTTP ${response.status}`);
|
|
510
|
+
}
|
|
511
|
+
const content = await response.text();
|
|
512
|
+
cacheSet(key, { fetchedAt: Date.now(), content });
|
|
513
|
+
return content;
|
|
514
|
+
}
|
|
515
|
+
function listSections(markdown) {
|
|
516
|
+
const sections = [];
|
|
517
|
+
for (const line of markdown.split("\n")) {
|
|
518
|
+
const match = line.match(/^##\s+(.+?)\s*$/);
|
|
519
|
+
if (match)
|
|
520
|
+
sections.push(match[1]);
|
|
521
|
+
}
|
|
522
|
+
return sections;
|
|
523
|
+
}
|
|
524
|
+
function extractSection(markdown, section) {
|
|
525
|
+
const lines = markdown.split("\n");
|
|
526
|
+
const wanted = section.trim().toLowerCase();
|
|
527
|
+
let start = -1;
|
|
528
|
+
for (let i = 0; i < lines.length; i++) {
|
|
529
|
+
const match = lines[i].match(/^##\s+(.+?)\s*$/);
|
|
530
|
+
if (match && match[1].toLowerCase() === wanted) {
|
|
531
|
+
start = i;
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (start === -1)
|
|
536
|
+
return void 0;
|
|
537
|
+
let end = lines.length;
|
|
538
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
539
|
+
if (/^##?\s+/.test(lines[i])) {
|
|
540
|
+
end = i;
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return lines.slice(start, end).join("\n").trimEnd();
|
|
545
|
+
}
|
|
546
|
+
async function getRobloxDoc(category, name, section) {
|
|
547
|
+
const markdown = await fetchRobloxDoc(category, name);
|
|
548
|
+
const sections = listSections(markdown);
|
|
549
|
+
if (section) {
|
|
550
|
+
const extracted = extractSection(markdown, section);
|
|
551
|
+
if (extracted === void 0) {
|
|
552
|
+
throw new Error(`Section "${section}" not found in ${category}/${name}. Available sections: ${sections.join(", ") || "(none)"}`);
|
|
553
|
+
}
|
|
554
|
+
return { content: extracted, truncated: false, sections };
|
|
555
|
+
}
|
|
556
|
+
if (markdown.length > MAX_DOC_CHARS) {
|
|
557
|
+
const head = markdown.slice(0, MAX_DOC_CHARS);
|
|
558
|
+
const note = `
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
[Truncated at ${MAX_DOC_CHARS} of ${markdown.length} characters. Re-request with the "section" parameter to read one section in full. Available sections: ${sections.join(", ")}]`;
|
|
562
|
+
return { content: head + note, truncated: true, sections };
|
|
563
|
+
}
|
|
564
|
+
return { content: markdown, truncated: false, sections };
|
|
565
|
+
}
|
|
566
|
+
var DOC_CATEGORIES, DOCS_BASE_URL, FETCH_TIMEOUT_MS, CACHE_TTL_MS, NEGATIVE_CACHE_TTL_MS, MAX_CACHE_ENTRIES, MAX_DOC_CHARS, cache, DocNotFoundError;
|
|
567
|
+
var init_roblox_docs = __esm({
|
|
568
|
+
"../core/dist/roblox-docs.js"() {
|
|
569
|
+
"use strict";
|
|
570
|
+
DOC_CATEGORIES = ["classes", "enums", "datatypes", "libraries", "globals"];
|
|
571
|
+
DOCS_BASE_URL = "https://create.roblox.com/docs/reference/engine";
|
|
572
|
+
FETCH_TIMEOUT_MS = 15e3;
|
|
573
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
574
|
+
NEGATIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
575
|
+
MAX_CACHE_ENTRIES = 200;
|
|
576
|
+
MAX_DOC_CHARS = 5e4;
|
|
577
|
+
cache = /* @__PURE__ */ new Map();
|
|
578
|
+
DocNotFoundError = class extends Error {
|
|
579
|
+
constructor(category, name) {
|
|
580
|
+
super(`No Roblox documentation found for ${category}/${name}. Names are case-sensitive PascalCase (e.g. "ProximityPrompt", "TweenService"). Valid categories: ${DOC_CATEGORIES.join(", ")}.`);
|
|
581
|
+
this.name = "DocNotFoundError";
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
454
587
|
// ../core/dist/mcp-compat.js
|
|
455
588
|
import { ErrorCode, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, McpError, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
456
|
-
function
|
|
589
|
+
function registerResourceHandlers(server) {
|
|
457
590
|
server.registerCapabilities({ resources: {} });
|
|
458
591
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
459
592
|
resources: []
|
|
460
593
|
}));
|
|
461
594
|
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
462
|
-
resourceTemplates: [
|
|
595
|
+
resourceTemplates: [
|
|
596
|
+
{
|
|
597
|
+
uriTemplate: "robloxdocs://classes/{className}",
|
|
598
|
+
name: "Roblox class documentation",
|
|
599
|
+
description: "Official engine reference for a class (e.g. robloxdocs://classes/ProximityPrompt) \u2014 properties, methods, events, and usage guidance as markdown.",
|
|
600
|
+
mimeType: "text/markdown"
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
uriTemplate: "robloxdocs://enums/{enumName}",
|
|
604
|
+
name: "Roblox enum documentation",
|
|
605
|
+
description: "Official engine reference for an enum (e.g. robloxdocs://enums/KeyCode).",
|
|
606
|
+
mimeType: "text/markdown"
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
uriTemplate: "robloxdocs://datatypes/{dataTypeName}",
|
|
610
|
+
name: "Roblox datatype documentation",
|
|
611
|
+
description: "Official engine reference for a datatype (e.g. robloxdocs://datatypes/CFrame).",
|
|
612
|
+
mimeType: "text/markdown"
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
uriTemplate: "robloxdocs://libraries/{libraryName}",
|
|
616
|
+
name: "Roblox library documentation",
|
|
617
|
+
description: "Official engine reference for a Luau library (e.g. robloxdocs://libraries/table).",
|
|
618
|
+
mimeType: "text/markdown"
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
uriTemplate: "robloxdocs://globals/{globalsPage}",
|
|
622
|
+
name: "Roblox globals documentation",
|
|
623
|
+
description: "Official engine reference for global functions and variables (e.g. robloxdocs://globals/LuaGlobals, robloxdocs://globals/RobloxGlobals).",
|
|
624
|
+
mimeType: "text/markdown"
|
|
625
|
+
}
|
|
626
|
+
]
|
|
463
627
|
}));
|
|
464
628
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
465
|
-
|
|
629
|
+
const uri = request.params.uri;
|
|
630
|
+
const match = uri.match(/^robloxdocs:\/\/([^/]+)\/([^/]+)$/);
|
|
631
|
+
if (!match || !isDocCategory(match[1])) {
|
|
632
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found`);
|
|
633
|
+
}
|
|
634
|
+
const [, category, rawName] = match;
|
|
635
|
+
const name = decodeURIComponent(rawName);
|
|
636
|
+
try {
|
|
637
|
+
const content = await fetchRobloxDoc(category, name);
|
|
638
|
+
return {
|
|
639
|
+
contents: [{ uri, mimeType: "text/markdown", text: content }]
|
|
640
|
+
};
|
|
641
|
+
} catch (error) {
|
|
642
|
+
if (error instanceof DocNotFoundError) {
|
|
643
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found. Names are case-sensitive PascalCase; valid categories: ${DOC_CATEGORIES.join(", ")}.`);
|
|
644
|
+
}
|
|
645
|
+
throw new McpError(ErrorCode.InternalError, `Failed to read ${uri}: ${error instanceof Error ? error.message : String(error)}`);
|
|
646
|
+
}
|
|
466
647
|
});
|
|
467
648
|
}
|
|
468
649
|
var init_mcp_compat = __esm({
|
|
469
650
|
"../core/dist/mcp-compat.js"() {
|
|
470
651
|
"use strict";
|
|
652
|
+
init_roblox_docs();
|
|
471
653
|
}
|
|
472
654
|
});
|
|
473
655
|
|
|
@@ -658,6 +840,15 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
658
840
|
}
|
|
659
841
|
res.json({ success: true });
|
|
660
842
|
});
|
|
843
|
+
app.post("/unregister-instance-id", (req, res) => {
|
|
844
|
+
const { instanceId } = req.body;
|
|
845
|
+
if (typeof instanceId !== "string" || instanceId.length === 0) {
|
|
846
|
+
res.status(400).json({ error: "instanceId is required" });
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
const removed = bridge.unregisterInstanceId(instanceId);
|
|
850
|
+
res.json({ success: true, removed });
|
|
851
|
+
});
|
|
661
852
|
app.get("/status", (req, res) => {
|
|
662
853
|
const instances = bridge.getInstances();
|
|
663
854
|
const publicInstances = instances.map(toPublic);
|
|
@@ -775,7 +966,7 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
775
966
|
try {
|
|
776
967
|
trackMCPActivity();
|
|
777
968
|
const server = new Server({ name: serverConfig.name, version: serverConfig.version }, { capabilities: { tools: {} } });
|
|
778
|
-
|
|
969
|
+
registerResourceHandlers(server);
|
|
779
970
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
780
971
|
tools: filteredTools.map((t) => ({
|
|
781
972
|
name: t.name,
|
|
@@ -918,6 +1109,7 @@ var init_http_server = __esm({
|
|
|
918
1109
|
init_bridge_service();
|
|
919
1110
|
init_mcp_compat();
|
|
920
1111
|
TOOL_HANDLERS = {
|
|
1112
|
+
get_roblox_docs: (tools, body) => tools.getRobloxDocs(body.name, body.doc_type, body.section),
|
|
921
1113
|
get_file_tree: (tools, body) => tools.getFileTree(body.path, body.instance_id),
|
|
922
1114
|
search_files: (tools, body) => tools.searchFiles(body.query, body.searchType, body.instance_id),
|
|
923
1115
|
get_place_info: (tools, body) => tools.getPlaceInfo(body.instance_id),
|
|
@@ -4388,6 +4580,7 @@ var init_tools = __esm({
|
|
|
4388
4580
|
init_roblox_cookie_client();
|
|
4389
4581
|
init_studio_instance_manager();
|
|
4390
4582
|
init_image_decode();
|
|
4583
|
+
init_roblox_docs();
|
|
4391
4584
|
init_jpeg_encoder();
|
|
4392
4585
|
init_png_encoder();
|
|
4393
4586
|
MAX_INLINE_IMAGE_BYTES = 6e6;
|
|
@@ -4459,6 +4652,17 @@ var init_tools = __esm({
|
|
|
4459
4652
|
_textResult(body) {
|
|
4460
4653
|
return { content: [{ type: "text", text: JSON.stringify(body) }] };
|
|
4461
4654
|
}
|
|
4655
|
+
async getRobloxDocs(name, docType, section) {
|
|
4656
|
+
if (!name || typeof name !== "string") {
|
|
4657
|
+
throw new Error('get_roblox_docs requires a name (e.g. "ProximityPrompt")');
|
|
4658
|
+
}
|
|
4659
|
+
const category = docType ?? "classes";
|
|
4660
|
+
if (!isDocCategory(category)) {
|
|
4661
|
+
throw new Error(`Invalid doc_type "${category}". Valid categories: ${DOC_CATEGORIES.join(", ")}`);
|
|
4662
|
+
}
|
|
4663
|
+
const result = await getRobloxDoc(category, name.trim(), section);
|
|
4664
|
+
return { content: [{ type: "text", text: result.content }] };
|
|
4665
|
+
}
|
|
4462
4666
|
_parseTextResult(result) {
|
|
4463
4667
|
const text = result?.content?.[0]?.text;
|
|
4464
4668
|
if (typeof text !== "string")
|
|
@@ -6094,9 +6298,9 @@ ${code}`
|
|
|
6094
6298
|
if (instance_id) {
|
|
6095
6299
|
const managedClose = this.instanceManager.closeByInstanceId(instance_id);
|
|
6096
6300
|
if (managedClose.status !== "not_found") {
|
|
6097
|
-
this.bridge.
|
|
6301
|
+
await this.bridge.unregisterInstanceIdEverywhere(instance_id);
|
|
6098
6302
|
await sleep(500);
|
|
6099
|
-
this.bridge.
|
|
6303
|
+
await this.bridge.unregisterInstanceIdEverywhere(instance_id);
|
|
6100
6304
|
return this._textResult({
|
|
6101
6305
|
instance_id,
|
|
6102
6306
|
message: managedClose.status === "already_closed" ? "Studio instance was already closed." : "Studio instance closed."
|
|
@@ -6119,7 +6323,7 @@ ${code}`
|
|
|
6119
6323
|
instance_id
|
|
6120
6324
|
});
|
|
6121
6325
|
}
|
|
6122
|
-
this.bridge.
|
|
6326
|
+
await this.bridge.unregisterInstanceIdEverywhere(instance_id);
|
|
6123
6327
|
return this._textResult({
|
|
6124
6328
|
instance_id,
|
|
6125
6329
|
message: "Studio instance closed."
|
|
@@ -6138,11 +6342,11 @@ ${code}`
|
|
|
6138
6342
|
record2 = active[0];
|
|
6139
6343
|
}
|
|
6140
6344
|
if (record2.instanceId)
|
|
6141
|
-
this.bridge.
|
|
6345
|
+
await this.bridge.unregisterInstanceIdEverywhere(record2.instanceId);
|
|
6142
6346
|
const closeResult = this.instanceManager.close(record2);
|
|
6143
6347
|
if (record2.instanceId) {
|
|
6144
6348
|
await sleep(500);
|
|
6145
|
-
this.bridge.
|
|
6349
|
+
await this.bridge.unregisterInstanceIdEverywhere(record2.instanceId);
|
|
6146
6350
|
}
|
|
6147
6351
|
return this._textResult({
|
|
6148
6352
|
instance_id: record2.instanceId,
|
|
@@ -7930,6 +8134,24 @@ var init_proxy_bridge_service = __esm({
|
|
|
7930
8134
|
getInstances() {
|
|
7931
8135
|
return this.cachedInstances;
|
|
7932
8136
|
}
|
|
8137
|
+
async unregisterInstanceIdEverywhere(instanceId) {
|
|
8138
|
+
const response = await fetch(`${this.primaryBaseUrl}/unregister-instance-id`, {
|
|
8139
|
+
method: "POST",
|
|
8140
|
+
headers: { "Content-Type": "application/json" },
|
|
8141
|
+
body: JSON.stringify({ instanceId })
|
|
8142
|
+
});
|
|
8143
|
+
if (!response.ok) {
|
|
8144
|
+
const body = await response.text().catch(() => "");
|
|
8145
|
+
throw new Error(`Proxy unregister failed (${response.status}): ${body || response.statusText}`);
|
|
8146
|
+
}
|
|
8147
|
+
const result = await response.json();
|
|
8148
|
+
const removed = Array.isArray(result.removed) ? result.removed : [];
|
|
8149
|
+
const removedKeys = new Set(removed.map((inst) => `${inst.instanceId}\0${inst.role}`));
|
|
8150
|
+
if (removedKeys.size > 0) {
|
|
8151
|
+
this.cachedInstances = this.cachedInstances.filter((inst) => !removedKeys.has(`${inst.instanceId}\0${inst.role}`));
|
|
8152
|
+
}
|
|
8153
|
+
return removed;
|
|
8154
|
+
}
|
|
7933
8155
|
/** Called when this proxy is being discarded (e.g. promotion to primary
|
|
7934
8156
|
replaced it). Stops the background refresh so it doesn't leak. */
|
|
7935
8157
|
stop() {
|
|
@@ -8012,7 +8234,7 @@ var init_server = __esm({
|
|
|
8012
8234
|
});
|
|
8013
8235
|
this.bridge = new BridgeService();
|
|
8014
8236
|
this.tools = new RobloxStudioTools(this.bridge);
|
|
8015
|
-
|
|
8237
|
+
registerResourceHandlers(this.server);
|
|
8016
8238
|
this.setupToolHandlers();
|
|
8017
8239
|
}
|
|
8018
8240
|
setupToolHandlers() {
|
|
@@ -10758,6 +10980,31 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
10758
10980
|
},
|
|
10759
10981
|
required: ["pattern", "replacement"]
|
|
10760
10982
|
}
|
|
10983
|
+
},
|
|
10984
|
+
// === Documentation ===
|
|
10985
|
+
{
|
|
10986
|
+
name: "get_roblox_docs",
|
|
10987
|
+
category: "read",
|
|
10988
|
+
description: 'Fetch official Roblox engine API documentation as markdown from create.roblox.com. Call this BEFORE writing or editing code that uses an engine class, enum, datatype, or Luau library you are not fully certain about (e.g. ProximityPrompt, Enum.KeyCode, CFrame, TweenService) \u2014 the page includes the description, properties, methods, events, and code samples. Results are cached, so repeat lookups are cheap. Very large pages are truncated with a section index; pass section (e.g. "Properties", "Methods", "Events") to read one section in full.',
|
|
10989
|
+
inputSchema: {
|
|
10990
|
+
type: "object",
|
|
10991
|
+
properties: {
|
|
10992
|
+
name: {
|
|
10993
|
+
type: "string",
|
|
10994
|
+
description: 'Exact PascalCase name of the class, enum, datatype, or library (e.g. "ProximityPrompt", "KeyCode", "CFrame", "table")'
|
|
10995
|
+
},
|
|
10996
|
+
doc_type: {
|
|
10997
|
+
type: "string",
|
|
10998
|
+
enum: ["classes", "enums", "datatypes", "libraries", "globals"],
|
|
10999
|
+
description: "Documentation category (default: classes)"
|
|
11000
|
+
},
|
|
11001
|
+
section: {
|
|
11002
|
+
type: "string",
|
|
11003
|
+
description: 'Optional "##"-level section to return instead of the whole page (e.g. "Description", "Properties", "Methods", "Events", "Code Samples")'
|
|
11004
|
+
}
|
|
11005
|
+
},
|
|
11006
|
+
required: ["name"]
|
|
11007
|
+
}
|
|
10761
11008
|
}
|
|
10762
11009
|
];
|
|
10763
11010
|
DEPRECATED_TOOL_DEFINITIONS = [
|
package/package.json
CHANGED
|
@@ -1413,9 +1413,9 @@ local function computeBridgeStamp()
|
|
|
1413
1413
|
for i = 1, #combined do
|
|
1414
1414
|
h = (h * 33 + (string.byte(combined, i))) % 2147483647
|
|
1415
1415
|
end
|
|
1416
|
-
-- "2.
|
|
1416
|
+
-- "2.20.0" is replaced with the package version at package time
|
|
1417
1417
|
-- (scripts/build-plugin.mjs injectVersion), so a release bump also restamps.
|
|
1418
|
-
return `{tostring(h)}-2.
|
|
1418
|
+
return `{tostring(h)}-2.20.0`
|
|
1419
1419
|
end
|
|
1420
1420
|
local BRIDGE_STAMP = computeBridgeStamp()
|
|
1421
1421
|
local function setSource(scriptInst, source)
|
|
@@ -10745,7 +10745,7 @@ return {
|
|
|
10745
10745
|
<Properties>
|
|
10746
10746
|
<string name="Name">State</string>
|
|
10747
10747
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
10748
|
-
local CURRENT_VERSION = "2.
|
|
10748
|
+
local CURRENT_VERSION = "2.20.0"
|
|
10749
10749
|
local PLUGIN_VARIANT = "inspector"
|
|
10750
10750
|
local BASE_PORT = 58741
|
|
10751
10751
|
local function createConnection(port)
|
|
@@ -1413,9 +1413,9 @@ local function computeBridgeStamp()
|
|
|
1413
1413
|
for i = 1, #combined do
|
|
1414
1414
|
h = (h * 33 + (string.byte(combined, i))) % 2147483647
|
|
1415
1415
|
end
|
|
1416
|
-
-- "2.
|
|
1416
|
+
-- "2.20.0" is replaced with the package version at package time
|
|
1417
1417
|
-- (scripts/build-plugin.mjs injectVersion), so a release bump also restamps.
|
|
1418
|
-
return `{tostring(h)}-2.
|
|
1418
|
+
return `{tostring(h)}-2.20.0`
|
|
1419
1419
|
end
|
|
1420
1420
|
local BRIDGE_STAMP = computeBridgeStamp()
|
|
1421
1421
|
local function setSource(scriptInst, source)
|
|
@@ -10745,7 +10745,7 @@ return {
|
|
|
10745
10745
|
<Properties>
|
|
10746
10746
|
<string name="Name">State</string>
|
|
10747
10747
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
10748
|
-
local CURRENT_VERSION = "2.
|
|
10748
|
+
local CURRENT_VERSION = "2.20.0"
|
|
10749
10749
|
local PLUGIN_VARIANT = "main"
|
|
10750
10750
|
local BASE_PORT = 58741
|
|
10751
10751
|
local function createConnection(port)
|