@chrrxs/robloxstudio-mcp 3.0.3 → 3.0.5
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 +203 -80
- package/package.json +1 -1
- package/studio-plugin/MCPPlugin.rbxmx +667 -340
|
@@ -16,6 +16,7 @@ local cleanupEditBridgeArtifacts = _EvalBridges.cleanupEditBridgeArtifacts
|
|
|
16
16
|
local ensureRuntimeBridgeInstalled = _EvalBridges.ensureRuntimeBridgeInstalled
|
|
17
17
|
local RuntimeLogBuffer = TS.import(script, script, "modules", "RuntimeLogBuffer")
|
|
18
18
|
local StopPlayMonitor = TS.import(script, script, "modules", "StopPlayMonitor")
|
|
19
|
+
local StudioEventStream = TS.import(script, script, "modules", "StudioEventStream")
|
|
19
20
|
local BreakpointHandlers = TS.import(script, script, "modules", "handlers", "BreakpointHandlers")
|
|
20
21
|
local RenderMonitor = TS.import(script, script, "modules", "RenderMonitor")
|
|
21
22
|
-- Track render-loop liveness so input/screenshot tools can report "window
|
|
@@ -32,8 +33,9 @@ RuntimeLogBuffer.install()
|
|
|
32
33
|
StopPlayMonitor.init(plugin)
|
|
33
34
|
BreakpointHandlers.init(plugin)
|
|
34
35
|
ServerUrlSettings.init(plugin)
|
|
36
|
+
local startupRole = ClientBroker.forkRole()
|
|
35
37
|
local function applyRememberedServerUrl()
|
|
36
|
-
if
|
|
38
|
+
if startupRole == "client" then
|
|
37
39
|
return nil
|
|
38
40
|
end
|
|
39
41
|
local rememberedServerUrl = ServerUrlSettings.readServerUrl()
|
|
@@ -49,6 +51,15 @@ local function applyRememberedServerUrl()
|
|
|
49
51
|
ClientBroker.setServerUrl(rememberedServerUrl)
|
|
50
52
|
end
|
|
51
53
|
applyRememberedServerUrl()
|
|
54
|
+
-- Play DataModels do not reliably fire Plugin.Unloading before Studio tears
|
|
55
|
+
-- down their VM. Close the persistent WebStreamClient while BindToClose still
|
|
56
|
+
-- gives this server peer a live execution context; otherwise each play cycle
|
|
57
|
+
-- can retain one native event stream until Studio itself is restarted. The
|
|
58
|
+
-- transport releases that native stream before yielding to unregister the
|
|
59
|
+
-- logical server peer.
|
|
60
|
+
if startupRole == "server" then
|
|
61
|
+
game:BindToClose(StudioEventStream.suspendForShutdown)
|
|
62
|
+
end
|
|
52
63
|
UI.init(plugin)
|
|
53
64
|
local elements = UI.getElements()
|
|
54
65
|
local ICON_DISCONNECTED = "rbxassetid://75876056391496"
|
|
@@ -91,7 +102,7 @@ task.delay(TOOLBAR_REGISTRATION_DELAY_SECONDS, registerToolbarButton)
|
|
|
91
102
|
-- play peers' plugin instances load without ever registering. Run after a
|
|
92
103
|
-- short delay so the UI/State have a chance to initialize first.
|
|
93
104
|
task.delay(2, function()
|
|
94
|
-
local role =
|
|
105
|
+
local role = startupRole
|
|
95
106
|
if role == "edit" then
|
|
96
107
|
cleanupEditBridgeArtifacts()
|
|
97
108
|
else
|
|
@@ -133,7 +144,10 @@ task.delay(2, function()
|
|
|
133
144
|
-- The play-server DM is the only one where StudioTestService:EndTest is
|
|
134
145
|
-- legal, so the stop-play monitor lives here. It consumes tokenized
|
|
135
146
|
-- stop requests from plugin settings and acknowledges EndTest results.
|
|
136
|
-
StopPlayMonitor.startMonitor(
|
|
147
|
+
StopPlayMonitor.startMonitor({
|
|
148
|
+
beforeEndTest = StudioEventStream.suspendForShutdown,
|
|
149
|
+
afterEndTestFailure = StudioEventStream.resumeAfterShutdownFailure,
|
|
150
|
+
})
|
|
137
151
|
elseif role == "client" then
|
|
138
152
|
ClientBroker.setupClientBroker()
|
|
139
153
|
end
|
|
@@ -880,12 +894,12 @@ local routeMap = {
|
|
|
880
894
|
["/api/get-memory-breakdown"] = MemoryHandlers.getMemoryBreakdown,
|
|
881
895
|
["/api/get-scene-analysis"] = SceneAnalysisHandlers.getSceneAnalysis,
|
|
882
896
|
}
|
|
883
|
-
local function processRequest(request)
|
|
897
|
+
local function processRequest(request, context)
|
|
884
898
|
local endpoint = request.endpoint
|
|
885
899
|
local data = request.data or {}
|
|
886
900
|
local handler = routeMap[endpoint]
|
|
887
901
|
if handler then
|
|
888
|
-
return handler(data)
|
|
902
|
+
return handler(data, context)
|
|
889
903
|
else
|
|
890
904
|
return {
|
|
891
905
|
error = `Unknown endpoint: {endpoint}`,
|
|
@@ -905,7 +919,7 @@ local function getConnectionStatus()
|
|
|
905
919
|
end
|
|
906
920
|
return "connecting"
|
|
907
921
|
end
|
|
908
|
-
local function dispatchStreamRequest(request)
|
|
922
|
+
local function dispatchStreamRequest(request, context)
|
|
909
923
|
if request.logicalSessionId ~= PluginSession.id then
|
|
910
924
|
return ClientBroker.dispatchClientRequest(request.logicalSessionId, request.target, request.endpoint, request.data)
|
|
911
925
|
end
|
|
@@ -922,7 +936,7 @@ local function dispatchStreamRequest(request)
|
|
|
922
936
|
return processRequest({
|
|
923
937
|
endpoint = request.endpoint,
|
|
924
938
|
data = request.data,
|
|
925
|
-
})
|
|
939
|
+
}, context)
|
|
926
940
|
end
|
|
927
941
|
local function handleReady(response)
|
|
928
942
|
local conn = State.getActiveConnection()
|
|
@@ -1154,6 +1168,81 @@ return {
|
|
|
1154
1168
|
</Properties>
|
|
1155
1169
|
</Item>
|
|
1156
1170
|
<Item class="ModuleScript" referent="5">
|
|
1171
|
+
<Properties>
|
|
1172
|
+
<string name="Name">CooperativeJobRunner</string>
|
|
1173
|
+
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
1174
|
+
local DEADLINE_EXCEEDED = "__RSMCP_COOPERATIVE_JOB_DEADLINE_EXCEEDED__"
|
|
1175
|
+
local CANCELLED = "__RSMCP_COOPERATIVE_JOB_CANCELLED__"
|
|
1176
|
+
local MAX_SLICE_SECONDS = 0.008
|
|
1177
|
+
local CLOCK_CHECK_INTERVAL = 64
|
|
1178
|
+
local activeJobs = {}
|
|
1179
|
+
local function runExclusive(key, execution, work)
|
|
1180
|
+
local _key = key
|
|
1181
|
+
local activeRequestId = activeJobs[_key]
|
|
1182
|
+
if activeRequestId ~= nil then
|
|
1183
|
+
return {
|
|
1184
|
+
error = "plugin_busy",
|
|
1185
|
+
activeRequestId = activeRequestId,
|
|
1186
|
+
}
|
|
1187
|
+
end
|
|
1188
|
+
local _key_1 = key
|
|
1189
|
+
local _requestId = execution.requestId
|
|
1190
|
+
activeJobs[_key_1] = _requestId
|
|
1191
|
+
local sliceStartedAt = os.clock()
|
|
1192
|
+
local operationsUntilClockCheck = 0
|
|
1193
|
+
local control = {
|
|
1194
|
+
checkpoint = function(self)
|
|
1195
|
+
local _result = execution.isCancelled
|
|
1196
|
+
if _result ~= nil then
|
|
1197
|
+
_result = _result()
|
|
1198
|
+
end
|
|
1199
|
+
if _result then
|
|
1200
|
+
error(CANCELLED, 0)
|
|
1201
|
+
end
|
|
1202
|
+
if operationsUntilClockCheck > 0 then
|
|
1203
|
+
operationsUntilClockCheck -= 1
|
|
1204
|
+
return nil
|
|
1205
|
+
end
|
|
1206
|
+
operationsUntilClockCheck = CLOCK_CHECK_INTERVAL - 1
|
|
1207
|
+
local now = os.clock()
|
|
1208
|
+
if execution.deadlineAt ~= nil and now >= execution.deadlineAt then
|
|
1209
|
+
error(DEADLINE_EXCEEDED, 0)
|
|
1210
|
+
end
|
|
1211
|
+
if now - sliceStartedAt >= MAX_SLICE_SECONDS then
|
|
1212
|
+
task.wait()
|
|
1213
|
+
sliceStartedAt = os.clock()
|
|
1214
|
+
end
|
|
1215
|
+
end,
|
|
1216
|
+
}
|
|
1217
|
+
local ok, result = pcall(function()
|
|
1218
|
+
return work(control)
|
|
1219
|
+
end)
|
|
1220
|
+
local _key_2 = key
|
|
1221
|
+
activeJobs[_key_2] = nil
|
|
1222
|
+
if not ok then
|
|
1223
|
+
if result == DEADLINE_EXCEEDED then
|
|
1224
|
+
return {
|
|
1225
|
+
error = "deadline_exceeded",
|
|
1226
|
+
requestId = execution.requestId,
|
|
1227
|
+
}
|
|
1228
|
+
end
|
|
1229
|
+
if result == CANCELLED then
|
|
1230
|
+
return {
|
|
1231
|
+
error = "cancelled",
|
|
1232
|
+
requestId = execution.requestId,
|
|
1233
|
+
}
|
|
1234
|
+
end
|
|
1235
|
+
error(result, 0)
|
|
1236
|
+
end
|
|
1237
|
+
return result
|
|
1238
|
+
end
|
|
1239
|
+
return {
|
|
1240
|
+
runExclusive = runExclusive,
|
|
1241
|
+
}
|
|
1242
|
+
]]></string>
|
|
1243
|
+
</Properties>
|
|
1244
|
+
</Item>
|
|
1245
|
+
<Item class="ModuleScript" referent="6">
|
|
1157
1246
|
<Properties>
|
|
1158
1247
|
<string name="Name">EvalBridges</string>
|
|
1159
1248
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -1273,9 +1362,9 @@ local function computeBridgeStamp()
|
|
|
1273
1362
|
for i = 1, #combined do
|
|
1274
1363
|
h = (h * 33 + (string.byte(combined, i))) % 2147483647
|
|
1275
1364
|
end
|
|
1276
|
-
-- "3.0.
|
|
1365
|
+
-- "3.0.5" is replaced with the package version at package time
|
|
1277
1366
|
-- (scripts/build-plugin.mjs injectVersion), so a release bump also restamps.
|
|
1278
|
-
return `{tostring(h)}-3.0.
|
|
1367
|
+
return `{tostring(h)}-3.0.5`
|
|
1279
1368
|
end
|
|
1280
1369
|
local BRIDGE_STAMP = computeBridgeStamp()
|
|
1281
1370
|
local function setSource(scriptInst, source)
|
|
@@ -1428,11 +1517,11 @@ return {
|
|
|
1428
1517
|
]]></string>
|
|
1429
1518
|
</Properties>
|
|
1430
1519
|
</Item>
|
|
1431
|
-
<Item class="Folder" referent="
|
|
1520
|
+
<Item class="Folder" referent="7">
|
|
1432
1521
|
<Properties>
|
|
1433
1522
|
<string name="Name">handlers</string>
|
|
1434
1523
|
</Properties>
|
|
1435
|
-
<Item class="ModuleScript" referent="
|
|
1524
|
+
<Item class="ModuleScript" referent="8">
|
|
1436
1525
|
<Properties>
|
|
1437
1526
|
<string name="Name">AssetHandlers</string>
|
|
1438
1527
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -1873,7 +1962,7 @@ return {
|
|
|
1873
1962
|
]]></string>
|
|
1874
1963
|
</Properties>
|
|
1875
1964
|
</Item>
|
|
1876
|
-
<Item class="ModuleScript" referent="
|
|
1965
|
+
<Item class="ModuleScript" referent="9">
|
|
1877
1966
|
<Properties>
|
|
1878
1967
|
<string name="Name">BreakpointHandlers</string>
|
|
1879
1968
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -2391,7 +2480,7 @@ return {
|
|
|
2391
2480
|
]]></string>
|
|
2392
2481
|
</Properties>
|
|
2393
2482
|
</Item>
|
|
2394
|
-
<Item class="ModuleScript" referent="
|
|
2483
|
+
<Item class="ModuleScript" referent="10">
|
|
2395
2484
|
<Properties>
|
|
2396
2485
|
<string name="Name">CaptureHandlers</string>
|
|
2397
2486
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -2635,7 +2724,7 @@ return {
|
|
|
2635
2724
|
]]></string>
|
|
2636
2725
|
</Properties>
|
|
2637
2726
|
</Item>
|
|
2638
|
-
<Item class="ModuleScript" referent="
|
|
2727
|
+
<Item class="ModuleScript" referent="11">
|
|
2639
2728
|
<Properties>
|
|
2640
2729
|
<string name="Name">EvalRuntimeHandlers</string>
|
|
2641
2730
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -2789,7 +2878,7 @@ return {
|
|
|
2789
2878
|
]]></string>
|
|
2790
2879
|
</Properties>
|
|
2791
2880
|
</Item>
|
|
2792
|
-
<Item class="ModuleScript" referent="
|
|
2881
|
+
<Item class="ModuleScript" referent="12">
|
|
2793
2882
|
<Properties>
|
|
2794
2883
|
<string name="Name">GenerateModelHandlers</string>
|
|
2795
2884
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -2994,7 +3083,7 @@ return {
|
|
|
2994
3083
|
]]></string>
|
|
2995
3084
|
</Properties>
|
|
2996
3085
|
</Item>
|
|
2997
|
-
<Item class="ModuleScript" referent="
|
|
3086
|
+
<Item class="ModuleScript" referent="13">
|
|
2998
3087
|
<Properties>
|
|
2999
3088
|
<string name="Name">InputHandlers</string>
|
|
3000
3089
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -3194,7 +3283,7 @@ return {
|
|
|
3194
3283
|
]]></string>
|
|
3195
3284
|
</Properties>
|
|
3196
3285
|
</Item>
|
|
3197
|
-
<Item class="ModuleScript" referent="
|
|
3286
|
+
<Item class="ModuleScript" referent="14">
|
|
3198
3287
|
<Properties>
|
|
3199
3288
|
<string name="Name">LogHandlers</string>
|
|
3200
3289
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -3220,7 +3309,7 @@ return {
|
|
|
3220
3309
|
]]></string>
|
|
3221
3310
|
</Properties>
|
|
3222
3311
|
</Item>
|
|
3223
|
-
<Item class="ModuleScript" referent="
|
|
3312
|
+
<Item class="ModuleScript" referent="15">
|
|
3224
3313
|
<Properties>
|
|
3225
3314
|
<string name="Name">MemoryHandlers</string>
|
|
3226
3315
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -3288,7 +3377,7 @@ return {
|
|
|
3288
3377
|
]]></string>
|
|
3289
3378
|
</Properties>
|
|
3290
3379
|
</Item>
|
|
3291
|
-
<Item class="ModuleScript" referent="
|
|
3380
|
+
<Item class="ModuleScript" referent="16">
|
|
3292
3381
|
<Properties>
|
|
3293
3382
|
<string name="Name">MetadataHandlers</string>
|
|
3294
3383
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -3619,7 +3708,7 @@ return {
|
|
|
3619
3708
|
]]></string>
|
|
3620
3709
|
</Properties>
|
|
3621
3710
|
</Item>
|
|
3622
|
-
<Item class="ModuleScript" referent="
|
|
3711
|
+
<Item class="ModuleScript" referent="17">
|
|
3623
3712
|
<Properties>
|
|
3624
3713
|
<string name="Name">MicroProfilerHandlers</string>
|
|
3625
3714
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -5372,7 +5461,7 @@ return {
|
|
|
5372
5461
|
]]></string>
|
|
5373
5462
|
</Properties>
|
|
5374
5463
|
</Item>
|
|
5375
|
-
<Item class="ModuleScript" referent="
|
|
5464
|
+
<Item class="ModuleScript" referent="18">
|
|
5376
5465
|
<Properties>
|
|
5377
5466
|
<string name="Name">PropertyHandlers</string>
|
|
5378
5467
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -5457,12 +5546,14 @@ return {
|
|
|
5457
5546
|
]]></string>
|
|
5458
5547
|
</Properties>
|
|
5459
5548
|
</Item>
|
|
5460
|
-
<Item class="ModuleScript" referent="
|
|
5549
|
+
<Item class="ModuleScript" referent="19">
|
|
5461
5550
|
<Properties>
|
|
5462
5551
|
<string name="Name">QueryHandlers</string>
|
|
5463
5552
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
5464
5553
|
local TS = require(script.Parent.Parent.Parent.include.RuntimeLib)
|
|
5465
5554
|
local Utils = TS.import(script, script.Parent.Parent, "Utils")
|
|
5555
|
+
local CooperativeJobRunner = TS.import(script, script.Parent.Parent, "CooperativeJobRunner")
|
|
5556
|
+
local ScriptSearch = TS.import(script, script.Parent.Parent, "ScriptSearch")
|
|
5466
5557
|
local _binding = Utils
|
|
5467
5558
|
local getInstancePath = _binding.getInstancePath
|
|
5468
5559
|
local getInstanceByPath = _binding.getInstanceByPath
|
|
@@ -6055,265 +6146,46 @@ local function getProjectStructure(requestData)
|
|
|
6055
6146
|
result.timestamp = tick()
|
|
6056
6147
|
return result
|
|
6057
6148
|
end
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
while i <= n do
|
|
6069
|
-
local c = string.sub(pattern, i, i)
|
|
6070
|
-
if c == "%" then
|
|
6071
|
-
if string.sub(pattern, i + 1, i + 1) == "b" then
|
|
6072
|
-
current ..= string.sub(pattern, i, math.min(i + 3, n))
|
|
6073
|
-
i += 4
|
|
6074
|
-
continue
|
|
6075
|
-
end
|
|
6076
|
-
-- Preserve an escape pair (e.g. %|, %., %d) intact.
|
|
6077
|
-
current ..= string.sub(pattern, i, i + 1)
|
|
6078
|
-
i += 2
|
|
6079
|
-
elseif c == "[" then
|
|
6080
|
-
inCharClass = true
|
|
6081
|
-
current ..= c
|
|
6082
|
-
i += 1
|
|
6083
|
-
elseif c == "]" then
|
|
6084
|
-
inCharClass = false
|
|
6085
|
-
current ..= c
|
|
6086
|
-
i += 1
|
|
6087
|
-
elseif c == "|" and not inCharClass then
|
|
6088
|
-
local _current = current
|
|
6089
|
-
table.insert(parts, _current)
|
|
6090
|
-
current = ""
|
|
6091
|
-
i += 1
|
|
6092
|
-
else
|
|
6093
|
-
current ..= c
|
|
6094
|
-
i += 1
|
|
6095
|
-
end
|
|
6096
|
-
end
|
|
6097
|
-
local _current = current
|
|
6098
|
-
table.insert(parts, _current)
|
|
6099
|
-
return parts
|
|
6100
|
-
end
|
|
6101
|
-
-- Return the earliest match across alternatives (mirrors regex alternation).
|
|
6102
|
-
local function findFirstPattern(line, alternatives)
|
|
6103
|
-
local bestStart
|
|
6104
|
-
local bestEnd
|
|
6105
|
-
for _, alt in alternatives do
|
|
6106
|
-
if alt == "" then
|
|
6107
|
-
continue
|
|
6108
|
-
end
|
|
6109
|
-
local s, e = string.find(line, alt)
|
|
6110
|
-
if s ~= nil and (bestStart == nil or s < bestStart) then
|
|
6111
|
-
bestStart = s
|
|
6112
|
-
bestEnd = e
|
|
6149
|
+
local scriptSearch = ScriptSearch.createScriptSearch({
|
|
6150
|
+
resolveRoot = function(self, path)
|
|
6151
|
+
return getInstanceByPath(path)
|
|
6152
|
+
end,
|
|
6153
|
+
getChildren = function(self, instance)
|
|
6154
|
+
return instance:GetChildren()
|
|
6155
|
+
end,
|
|
6156
|
+
readScript = function(self, instance, classFilter)
|
|
6157
|
+
if not instance:IsA("LuaSourceContainer") or (classFilter ~= nil and instance.ClassName ~= classFilter) then
|
|
6158
|
+
return nil
|
|
6113
6159
|
end
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
if not (pattern ~= "" and pattern) then
|
|
6120
|
-
return {
|
|
6121
|
-
error = "pattern is required",
|
|
6122
|
-
}
|
|
6123
|
-
end
|
|
6124
|
-
local _condition = (requestData.usePattern)
|
|
6125
|
-
if _condition == nil then
|
|
6126
|
-
_condition = false
|
|
6127
|
-
end
|
|
6128
|
-
local usePattern = _condition
|
|
6129
|
-
if usePattern and requestData.caseSensitive == false then
|
|
6130
|
-
return {
|
|
6131
|
-
error = "Case-insensitive Lua pattern search is not supported. Omit caseSensitive or pass caseSensitive: true with usePattern: true, or use literal search.",
|
|
6160
|
+
local snapshot = {
|
|
6161
|
+
instancePath = getInstancePath(instance),
|
|
6162
|
+
name = instance.Name,
|
|
6163
|
+
className = instance.ClassName,
|
|
6164
|
+
source = readScriptSource(instance),
|
|
6132
6165
|
}
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
if usePattern then
|
|
6136
|
-
_result = true
|
|
6137
|
-
else
|
|
6138
|
-
local _condition_1 = (requestData.caseSensitive)
|
|
6139
|
-
if _condition_1 == nil then
|
|
6140
|
-
_condition_1 = false
|
|
6166
|
+
if instance:IsA("BaseScript") then
|
|
6167
|
+
snapshot.enabled = instance.Enabled
|
|
6141
6168
|
end
|
|
6142
|
-
|
|
6143
|
-
end
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
end
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
local maxResults = _condition_2
|
|
6155
|
-
local _condition_3 = (requestData.maxResultsPerScript)
|
|
6156
|
-
if _condition_3 == nil then
|
|
6157
|
-
_condition_3 = 0
|
|
6158
|
-
end
|
|
6159
|
-
local maxResultsPerScript = _condition_3
|
|
6160
|
-
local _condition_4 = (requestData.filesOnly)
|
|
6161
|
-
if _condition_4 == nil then
|
|
6162
|
-
_condition_4 = false
|
|
6163
|
-
end
|
|
6164
|
-
local filesOnly = _condition_4
|
|
6165
|
-
local _condition_5 = (requestData.path)
|
|
6166
|
-
if _condition_5 == nil then
|
|
6167
|
-
_condition_5 = ""
|
|
6168
|
-
end
|
|
6169
|
-
local searchPath = _condition_5
|
|
6170
|
-
local classFilter = requestData.classFilter
|
|
6171
|
-
local startInstance = if searchPath ~= "" then getInstanceByPath(searchPath) else game
|
|
6172
|
-
if not startInstance then
|
|
6173
|
-
return {
|
|
6174
|
-
error = `Path not found: {searchPath}`,
|
|
6175
|
-
}
|
|
6169
|
+
return snapshot
|
|
6170
|
+
end,
|
|
6171
|
+
})
|
|
6172
|
+
local function grepScripts(requestData, execution)
|
|
6173
|
+
local result = CooperativeJobRunner.runExclusive("script-source-search", execution, function(control)
|
|
6174
|
+
return scriptSearch.search(requestData, control)
|
|
6175
|
+
end)
|
|
6176
|
+
if result.error == "plugin_busy" then
|
|
6177
|
+
local _object = table.clone(result)
|
|
6178
|
+
setmetatable(_object, nil)
|
|
6179
|
+
_object.message = "Another grep_scripts request is already running in this Studio DataModel. Retry after it completes."
|
|
6180
|
+
return _object
|
|
6176
6181
|
end
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
local totalMatches = 0
|
|
6183
|
-
local scriptsSearched = 0
|
|
6184
|
-
local hitLimit = false
|
|
6185
|
-
local function searchInstance(instance)
|
|
6186
|
-
if hitLimit then
|
|
6187
|
-
return nil
|
|
6188
|
-
end
|
|
6189
|
-
if instance:IsA("LuaSourceContainer") then
|
|
6190
|
-
-- Apply class filter
|
|
6191
|
-
if classFilter ~= "" and classFilter then
|
|
6192
|
-
local _exp = string.lower(instance.ClassName)
|
|
6193
|
-
local _arg0 = string.lower(classFilter)
|
|
6194
|
-
local _value = (string.find(_exp, _arg0))
|
|
6195
|
-
if not (_value ~= 0 and _value == _value and _value) then
|
|
6196
|
-
return nil
|
|
6197
|
-
end
|
|
6198
|
-
end
|
|
6199
|
-
scriptsSearched += 1
|
|
6200
|
-
local source = readScriptSource(instance)
|
|
6201
|
-
local lines = Utils.splitLines(source)
|
|
6202
|
-
local scriptMatches = {}
|
|
6203
|
-
local scriptMatchCount = 0
|
|
6204
|
-
for i = 0, #lines - 1 do
|
|
6205
|
-
if hitLimit then
|
|
6206
|
-
break
|
|
6207
|
-
end
|
|
6208
|
-
if maxResultsPerScript > 0 and scriptMatchCount >= maxResultsPerScript then
|
|
6209
|
-
break
|
|
6210
|
-
end
|
|
6211
|
-
local line = lines[i + 1]
|
|
6212
|
-
local searchLine = if caseSensitive then line else string.lower(line)
|
|
6213
|
-
local matchStart
|
|
6214
|
-
local matchEnd
|
|
6215
|
-
if usePattern then
|
|
6216
|
-
local _binding_1 = findFirstPattern(searchLine, patternAlternatives)
|
|
6217
|
-
matchStart = _binding_1[1]
|
|
6218
|
-
matchEnd = _binding_1[2]
|
|
6219
|
-
else
|
|
6220
|
-
matchStart, matchEnd = string.find(searchLine, searchPattern, 1, true)
|
|
6221
|
-
end
|
|
6222
|
-
if matchStart ~= nil then
|
|
6223
|
-
scriptMatchCount += 1
|
|
6224
|
-
totalMatches += 1
|
|
6225
|
-
if totalMatches > maxResults then
|
|
6226
|
-
hitLimit = true
|
|
6227
|
-
break
|
|
6228
|
-
end
|
|
6229
|
-
if not filesOnly then
|
|
6230
|
-
-- Gather context lines
|
|
6231
|
-
local before = {}
|
|
6232
|
-
local after = {}
|
|
6233
|
-
if contextLines > 0 then
|
|
6234
|
-
local beforeStart = math.max(0, i - contextLines)
|
|
6235
|
-
do
|
|
6236
|
-
local j = beforeStart
|
|
6237
|
-
local _shouldIncrement = false
|
|
6238
|
-
while true do
|
|
6239
|
-
if _shouldIncrement then
|
|
6240
|
-
j += 1
|
|
6241
|
-
else
|
|
6242
|
-
_shouldIncrement = true
|
|
6243
|
-
end
|
|
6244
|
-
if not (j < i) then
|
|
6245
|
-
break
|
|
6246
|
-
end
|
|
6247
|
-
local _arg0 = lines[j + 1]
|
|
6248
|
-
table.insert(before, _arg0)
|
|
6249
|
-
end
|
|
6250
|
-
end
|
|
6251
|
-
local afterEnd = math.min(#lines - 1, i + contextLines)
|
|
6252
|
-
do
|
|
6253
|
-
local j = i + 1
|
|
6254
|
-
local _shouldIncrement = false
|
|
6255
|
-
while true do
|
|
6256
|
-
if _shouldIncrement then
|
|
6257
|
-
j += 1
|
|
6258
|
-
else
|
|
6259
|
-
_shouldIncrement = true
|
|
6260
|
-
end
|
|
6261
|
-
if not (j <= afterEnd) then
|
|
6262
|
-
break
|
|
6263
|
-
end
|
|
6264
|
-
local _arg0 = lines[j + 1]
|
|
6265
|
-
table.insert(after, _arg0)
|
|
6266
|
-
end
|
|
6267
|
-
end
|
|
6268
|
-
end
|
|
6269
|
-
local _arg0 = {
|
|
6270
|
-
line = i + 1,
|
|
6271
|
-
column = matchStart,
|
|
6272
|
-
text = line,
|
|
6273
|
-
before = before,
|
|
6274
|
-
after = after,
|
|
6275
|
-
}
|
|
6276
|
-
table.insert(scriptMatches, _arg0)
|
|
6277
|
-
end
|
|
6278
|
-
end
|
|
6279
|
-
end
|
|
6280
|
-
if scriptMatchCount > 0 then
|
|
6281
|
-
local scriptResult = {
|
|
6282
|
-
instancePath = getInstancePath(instance),
|
|
6283
|
-
name = instance.Name,
|
|
6284
|
-
className = instance.ClassName,
|
|
6285
|
-
matches = scriptMatches,
|
|
6286
|
-
}
|
|
6287
|
-
if instance:IsA("BaseScript") then
|
|
6288
|
-
scriptResult.enabled = instance.Enabled
|
|
6289
|
-
end
|
|
6290
|
-
table.insert(results, scriptResult)
|
|
6291
|
-
end
|
|
6292
|
-
end
|
|
6293
|
-
for _, child in instance:GetChildren() do
|
|
6294
|
-
if hitLimit then
|
|
6295
|
-
return nil
|
|
6296
|
-
end
|
|
6297
|
-
searchInstance(child)
|
|
6298
|
-
end
|
|
6182
|
+
if result.error == "deadline_exceeded" then
|
|
6183
|
+
local _object = table.clone(result)
|
|
6184
|
+
setmetatable(_object, nil)
|
|
6185
|
+
_object.message = "grep_scripts exceeded its bridge deadline before the scan completed."
|
|
6186
|
+
return _object
|
|
6299
6187
|
end
|
|
6300
|
-
|
|
6301
|
-
return {
|
|
6302
|
-
results = results,
|
|
6303
|
-
pattern = pattern,
|
|
6304
|
-
totalMatches = if hitLimit then `>{maxResults}` else totalMatches,
|
|
6305
|
-
scriptsSearched = scriptsSearched,
|
|
6306
|
-
scriptsMatched = #results,
|
|
6307
|
-
truncated = hitLimit,
|
|
6308
|
-
options = {
|
|
6309
|
-
caseSensitive = caseSensitive,
|
|
6310
|
-
contextLines = contextLines,
|
|
6311
|
-
usePattern = usePattern,
|
|
6312
|
-
filesOnly = filesOnly,
|
|
6313
|
-
maxResults = maxResults,
|
|
6314
|
-
maxResultsPerScript = maxResultsPerScript,
|
|
6315
|
-
},
|
|
6316
|
-
}
|
|
6188
|
+
return result
|
|
6317
6189
|
end
|
|
6318
6190
|
return {
|
|
6319
6191
|
getFileTree = getFileTree,
|
|
@@ -6329,7 +6201,7 @@ return {
|
|
|
6329
6201
|
]]></string>
|
|
6330
6202
|
</Properties>
|
|
6331
6203
|
</Item>
|
|
6332
|
-
<Item class="ModuleScript" referent="
|
|
6204
|
+
<Item class="ModuleScript" referent="20">
|
|
6333
6205
|
<Properties>
|
|
6334
6206
|
<string name="Name">SceneAnalysisHandlers</string>
|
|
6335
6207
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -6578,7 +6450,7 @@ return {
|
|
|
6578
6450
|
]]></string>
|
|
6579
6451
|
</Properties>
|
|
6580
6452
|
</Item>
|
|
6581
|
-
<Item class="ModuleScript" referent="
|
|
6453
|
+
<Item class="ModuleScript" referent="21">
|
|
6582
6454
|
<Properties>
|
|
6583
6455
|
<string name="Name">ScriptHandlers</string>
|
|
6584
6456
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -7250,7 +7122,7 @@ return {
|
|
|
7250
7122
|
]]></string>
|
|
7251
7123
|
</Properties>
|
|
7252
7124
|
</Item>
|
|
7253
|
-
<Item class="ModuleScript" referent="
|
|
7125
|
+
<Item class="ModuleScript" referent="22">
|
|
7254
7126
|
<Properties>
|
|
7255
7127
|
<string name="Name">ScriptProfilerHandlers</string>
|
|
7256
7128
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -7713,7 +7585,7 @@ return {
|
|
|
7713
7585
|
]]></string>
|
|
7714
7586
|
</Properties>
|
|
7715
7587
|
</Item>
|
|
7716
|
-
<Item class="ModuleScript" referent="
|
|
7588
|
+
<Item class="ModuleScript" referent="23">
|
|
7717
7589
|
<Properties>
|
|
7718
7590
|
<string name="Name">SerializationHandlers</string>
|
|
7719
7591
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -7899,7 +7771,7 @@ return {
|
|
|
7899
7771
|
]]></string>
|
|
7900
7772
|
</Properties>
|
|
7901
7773
|
</Item>
|
|
7902
|
-
<Item class="ModuleScript" referent="
|
|
7774
|
+
<Item class="ModuleScript" referent="24">
|
|
7903
7775
|
<Properties>
|
|
7904
7776
|
<string name="Name">TestHandlers</string>
|
|
7905
7777
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -8278,7 +8150,7 @@ return {
|
|
|
8278
8150
|
</Properties>
|
|
8279
8151
|
</Item>
|
|
8280
8152
|
</Item>
|
|
8281
|
-
<Item class="ModuleScript" referent="
|
|
8153
|
+
<Item class="ModuleScript" referent="25">
|
|
8282
8154
|
<Properties>
|
|
8283
8155
|
<string name="Name">HttpDiagnostics</string>
|
|
8284
8156
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -8359,7 +8231,7 @@ return {
|
|
|
8359
8231
|
]]></string>
|
|
8360
8232
|
</Properties>
|
|
8361
8233
|
</Item>
|
|
8362
|
-
<Item class="ModuleScript" referent="
|
|
8234
|
+
<Item class="ModuleScript" referent="26">
|
|
8363
8235
|
<Properties>
|
|
8364
8236
|
<string name="Name">LuauExec</string>
|
|
8365
8237
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -8793,7 +8665,7 @@ return {
|
|
|
8793
8665
|
]]></string>
|
|
8794
8666
|
</Properties>
|
|
8795
8667
|
</Item>
|
|
8796
|
-
<Item class="ModuleScript" referent="
|
|
8668
|
+
<Item class="ModuleScript" referent="27">
|
|
8797
8669
|
<Properties>
|
|
8798
8670
|
<string name="Name">PluginSession</string>
|
|
8799
8671
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -8885,7 +8757,7 @@ return {
|
|
|
8885
8757
|
]]></string>
|
|
8886
8758
|
</Properties>
|
|
8887
8759
|
</Item>
|
|
8888
|
-
<Item class="ModuleScript" referent="
|
|
8760
|
+
<Item class="ModuleScript" referent="28">
|
|
8889
8761
|
<Properties>
|
|
8890
8762
|
<string name="Name">Recording</string>
|
|
8891
8763
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -8915,7 +8787,7 @@ return {
|
|
|
8915
8787
|
]]></string>
|
|
8916
8788
|
</Properties>
|
|
8917
8789
|
</Item>
|
|
8918
|
-
<Item class="ModuleScript" referent="
|
|
8790
|
+
<Item class="ModuleScript" referent="29">
|
|
8919
8791
|
<Properties>
|
|
8920
8792
|
<string name="Name">RenderMonitor</string>
|
|
8921
8793
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -8983,7 +8855,7 @@ return {
|
|
|
8983
8855
|
]]></string>
|
|
8984
8856
|
</Properties>
|
|
8985
8857
|
</Item>
|
|
8986
|
-
<Item class="ModuleScript" referent="
|
|
8858
|
+
<Item class="ModuleScript" referent="30">
|
|
8987
8859
|
<Properties>
|
|
8988
8860
|
<string name="Name">RuntimeLogBuffer</string>
|
|
8989
8861
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -9238,7 +9110,334 @@ return {
|
|
|
9238
9110
|
]]></string>
|
|
9239
9111
|
</Properties>
|
|
9240
9112
|
</Item>
|
|
9241
|
-
<Item class="ModuleScript" referent="
|
|
9113
|
+
<Item class="ModuleScript" referent="31">
|
|
9114
|
+
<Properties>
|
|
9115
|
+
<string name="Name">ScriptSearch</string>
|
|
9116
|
+
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
9117
|
+
local function splitLuaAlternation(pattern)
|
|
9118
|
+
local parts = {}
|
|
9119
|
+
local current = ""
|
|
9120
|
+
local index = 1
|
|
9121
|
+
local inCharacterClass = false
|
|
9122
|
+
while index <= #pattern do
|
|
9123
|
+
local character = string.sub(pattern, index, index)
|
|
9124
|
+
if character == "%" then
|
|
9125
|
+
if string.sub(pattern, index + 1, index + 1) == "b" then
|
|
9126
|
+
current ..= string.sub(pattern, index, math.min(index + 3, #pattern))
|
|
9127
|
+
index += 4
|
|
9128
|
+
else
|
|
9129
|
+
current ..= string.sub(pattern, index, index + 1)
|
|
9130
|
+
index += 2
|
|
9131
|
+
end
|
|
9132
|
+
elseif character == "[" then
|
|
9133
|
+
inCharacterClass = true
|
|
9134
|
+
current ..= character
|
|
9135
|
+
index += 1
|
|
9136
|
+
elseif character == "]" then
|
|
9137
|
+
inCharacterClass = false
|
|
9138
|
+
current ..= character
|
|
9139
|
+
index += 1
|
|
9140
|
+
elseif character == "|" and not inCharacterClass then
|
|
9141
|
+
local _current = current
|
|
9142
|
+
table.insert(parts, _current)
|
|
9143
|
+
current = ""
|
|
9144
|
+
index += 1
|
|
9145
|
+
else
|
|
9146
|
+
current ..= character
|
|
9147
|
+
index += 1
|
|
9148
|
+
end
|
|
9149
|
+
end
|
|
9150
|
+
local _current = current
|
|
9151
|
+
table.insert(parts, _current)
|
|
9152
|
+
return parts
|
|
9153
|
+
end
|
|
9154
|
+
local function findFirstPattern(line, alternatives, control)
|
|
9155
|
+
local earliest
|
|
9156
|
+
for _, alternative in alternatives do
|
|
9157
|
+
control:checkpoint()
|
|
9158
|
+
if alternative == "" then
|
|
9159
|
+
continue
|
|
9160
|
+
end
|
|
9161
|
+
local start = string.find(line, alternative)
|
|
9162
|
+
if start ~= nil and (earliest == nil or start < earliest) then
|
|
9163
|
+
earliest = start
|
|
9164
|
+
end
|
|
9165
|
+
end
|
|
9166
|
+
return earliest
|
|
9167
|
+
end
|
|
9168
|
+
local function readLine(source, start)
|
|
9169
|
+
local boundary = string.find(source, "[\r\n]", start)
|
|
9170
|
+
if boundary == nil then
|
|
9171
|
+
return { string.sub(source, start), nil }
|
|
9172
|
+
end
|
|
9173
|
+
local nextStart = boundary + 1
|
|
9174
|
+
if string.sub(source, boundary, boundary) == "\r" and string.sub(source, nextStart, nextStart) == "\n" then
|
|
9175
|
+
nextStart += 1
|
|
9176
|
+
end
|
|
9177
|
+
return { string.sub(source, start, boundary - 1), if nextStart <= #source then nextStart else nil }
|
|
9178
|
+
end
|
|
9179
|
+
local MAX_RESULTS = 10_000
|
|
9180
|
+
local LITERAL_CHUNK_BYTES = 64 * 1024
|
|
9181
|
+
local MAX_PATTERN_BYTES = 4096
|
|
9182
|
+
local MAX_CONTEXT_LINES = 100
|
|
9183
|
+
local function sourceCanMatchLiteral(source, pattern, caseSensitive, control)
|
|
9184
|
+
if #source == 0 then
|
|
9185
|
+
return false
|
|
9186
|
+
end
|
|
9187
|
+
local overlapBytes = math.max(#pattern - 1, 0)
|
|
9188
|
+
local chunkStart = 1
|
|
9189
|
+
while chunkStart <= #source do
|
|
9190
|
+
control:checkpoint()
|
|
9191
|
+
local chunkEnd = math.min(#source, chunkStart + LITERAL_CHUNK_BYTES + overlapBytes - 1)
|
|
9192
|
+
local chunk = string.sub(source, chunkStart, chunkEnd)
|
|
9193
|
+
local candidate = if caseSensitive then chunk else string.lower(chunk)
|
|
9194
|
+
if (string.find(candidate, pattern, 1, true)) ~= nil then
|
|
9195
|
+
return true
|
|
9196
|
+
end
|
|
9197
|
+
if chunkEnd >= #source then
|
|
9198
|
+
return false
|
|
9199
|
+
end
|
|
9200
|
+
chunkStart += LITERAL_CHUNK_BYTES
|
|
9201
|
+
end
|
|
9202
|
+
return false
|
|
9203
|
+
end
|
|
9204
|
+
local function createScriptSearch(corpus)
|
|
9205
|
+
local function search(requestData, control)
|
|
9206
|
+
local requestedPattern = requestData.pattern
|
|
9207
|
+
if not (type(requestedPattern) == "string") or requestedPattern == "" then
|
|
9208
|
+
return {
|
|
9209
|
+
error = "pattern is required",
|
|
9210
|
+
}
|
|
9211
|
+
end
|
|
9212
|
+
if #requestedPattern > MAX_PATTERN_BYTES then
|
|
9213
|
+
return {
|
|
9214
|
+
error = "invalid_request",
|
|
9215
|
+
message = `pattern must contain between 1 and {MAX_PATTERN_BYTES} bytes`,
|
|
9216
|
+
}
|
|
9217
|
+
end
|
|
9218
|
+
local pattern = requestedPattern
|
|
9219
|
+
for _, optionName in { "usePattern", "caseSensitive", "filesOnly" } do
|
|
9220
|
+
local option = requestData[optionName]
|
|
9221
|
+
if option ~= nil and not (type(option) == "boolean") then
|
|
9222
|
+
return {
|
|
9223
|
+
error = "invalid_request",
|
|
9224
|
+
message = `{optionName} must be a boolean`,
|
|
9225
|
+
}
|
|
9226
|
+
end
|
|
9227
|
+
end
|
|
9228
|
+
local _condition = (requestData.usePattern)
|
|
9229
|
+
if _condition == nil then
|
|
9230
|
+
_condition = false
|
|
9231
|
+
end
|
|
9232
|
+
local usePattern = _condition
|
|
9233
|
+
if usePattern and requestData.caseSensitive == false then
|
|
9234
|
+
return {
|
|
9235
|
+
error = "Case-insensitive Lua pattern search is not supported.",
|
|
9236
|
+
}
|
|
9237
|
+
end
|
|
9238
|
+
local _result
|
|
9239
|
+
if usePattern then
|
|
9240
|
+
_result = true
|
|
9241
|
+
else
|
|
9242
|
+
local _condition_1 = (requestData.caseSensitive)
|
|
9243
|
+
if _condition_1 == nil then
|
|
9244
|
+
_condition_1 = false
|
|
9245
|
+
end
|
|
9246
|
+
_result = _condition_1
|
|
9247
|
+
end
|
|
9248
|
+
local caseSensitive = _result
|
|
9249
|
+
local _condition_1 = (requestData.filesOnly)
|
|
9250
|
+
if _condition_1 == nil then
|
|
9251
|
+
_condition_1 = false
|
|
9252
|
+
end
|
|
9253
|
+
local filesOnly = _condition_1
|
|
9254
|
+
local requestedContextLines = requestData.contextLines
|
|
9255
|
+
if requestedContextLines ~= nil and (not (type(requestedContextLines) == "number") or math.floor(requestedContextLines) ~= requestedContextLines or requestedContextLines < 0 or requestedContextLines > MAX_CONTEXT_LINES) then
|
|
9256
|
+
return {
|
|
9257
|
+
error = "invalid_request",
|
|
9258
|
+
message = `contextLines must be an integer between 0 and {MAX_CONTEXT_LINES}`,
|
|
9259
|
+
}
|
|
9260
|
+
end
|
|
9261
|
+
local _condition_2 = requestedContextLines
|
|
9262
|
+
if _condition_2 == nil then
|
|
9263
|
+
_condition_2 = 0
|
|
9264
|
+
end
|
|
9265
|
+
local contextLines = _condition_2
|
|
9266
|
+
local requestedMaxResults = requestData.maxResults
|
|
9267
|
+
if requestedMaxResults ~= nil and (not (type(requestedMaxResults) == "number") or math.floor(requestedMaxResults) ~= requestedMaxResults or requestedMaxResults < 1 or requestedMaxResults > MAX_RESULTS) then
|
|
9268
|
+
return {
|
|
9269
|
+
error = "invalid_request",
|
|
9270
|
+
message = `maxResults must be an integer between 1 and {MAX_RESULTS}`,
|
|
9271
|
+
}
|
|
9272
|
+
end
|
|
9273
|
+
local _condition_3 = requestedMaxResults
|
|
9274
|
+
if _condition_3 == nil then
|
|
9275
|
+
_condition_3 = 100
|
|
9276
|
+
end
|
|
9277
|
+
local maxResults = _condition_3
|
|
9278
|
+
local requestedMaxResultsPerScript = requestData.maxResultsPerScript
|
|
9279
|
+
if requestedMaxResultsPerScript ~= nil and (not (type(requestedMaxResultsPerScript) == "number") or math.floor(requestedMaxResultsPerScript) ~= requestedMaxResultsPerScript or requestedMaxResultsPerScript < 0 or requestedMaxResultsPerScript > MAX_RESULTS) then
|
|
9280
|
+
return {
|
|
9281
|
+
error = "invalid_request",
|
|
9282
|
+
message = `maxResultsPerScript must be an integer between 0 and {MAX_RESULTS}`,
|
|
9283
|
+
}
|
|
9284
|
+
end
|
|
9285
|
+
local _condition_4 = requestedMaxResultsPerScript
|
|
9286
|
+
if _condition_4 == nil then
|
|
9287
|
+
_condition_4 = 0
|
|
9288
|
+
end
|
|
9289
|
+
local maxResultsPerScript = _condition_4
|
|
9290
|
+
local classFilter = requestData.classFilter
|
|
9291
|
+
local _condition_5 = (requestData.path)
|
|
9292
|
+
if _condition_5 == nil then
|
|
9293
|
+
_condition_5 = ""
|
|
9294
|
+
end
|
|
9295
|
+
local searchPath = _condition_5
|
|
9296
|
+
local root = corpus:resolveRoot(searchPath)
|
|
9297
|
+
if root == nil then
|
|
9298
|
+
return {
|
|
9299
|
+
error = `Path not found: {searchPath}`,
|
|
9300
|
+
}
|
|
9301
|
+
end
|
|
9302
|
+
local searchPattern = if caseSensitive then pattern else string.lower(pattern)
|
|
9303
|
+
local patternAlternatives = if usePattern then splitLuaAlternation(searchPattern) else nil
|
|
9304
|
+
local results = {}
|
|
9305
|
+
local stack = { root }
|
|
9306
|
+
local totalMatches = 0
|
|
9307
|
+
local scriptsSearched = 0
|
|
9308
|
+
local hitLimit = false
|
|
9309
|
+
while #stack > 0 and not hitLimit do
|
|
9310
|
+
control:checkpoint()
|
|
9311
|
+
-- ▼ Array.pop ▼
|
|
9312
|
+
local _length = #stack
|
|
9313
|
+
local _result_1 = stack[_length]
|
|
9314
|
+
stack[_length] = nil
|
|
9315
|
+
-- ▲ Array.pop ▲
|
|
9316
|
+
local instance = _result_1
|
|
9317
|
+
local snapshot = corpus:readScript(instance, classFilter)
|
|
9318
|
+
if snapshot ~= nil and (classFilter == nil or snapshot.className == classFilter) then
|
|
9319
|
+
scriptsSearched += 1
|
|
9320
|
+
local scriptMatches = {}
|
|
9321
|
+
local before = {}
|
|
9322
|
+
local pendingAfter = {}
|
|
9323
|
+
local scriptMatchCount = 0
|
|
9324
|
+
local lineNumber = 1
|
|
9325
|
+
local lineStart = 1
|
|
9326
|
+
local canMatch = usePattern or sourceCanMatchLiteral(snapshot.source, searchPattern, caseSensitive, control)
|
|
9327
|
+
if canMatch then
|
|
9328
|
+
while lineStart ~= nil and not hitLimit do
|
|
9329
|
+
control:checkpoint()
|
|
9330
|
+
local _binding = readLine(snapshot.source, lineStart)
|
|
9331
|
+
local line = _binding[1]
|
|
9332
|
+
local nextLineStart = _binding[2]
|
|
9333
|
+
for _, pending in pendingAfter do
|
|
9334
|
+
if pending.remaining > 0 then
|
|
9335
|
+
local _exp = pending.match.after
|
|
9336
|
+
table.insert(_exp, line)
|
|
9337
|
+
pending.remaining -= 1
|
|
9338
|
+
end
|
|
9339
|
+
end
|
|
9340
|
+
while #pendingAfter > 0 and pendingAfter[1].remaining == 0 do
|
|
9341
|
+
table.remove(pendingAfter, 1)
|
|
9342
|
+
end
|
|
9343
|
+
local candidate = if caseSensitive then line else string.lower(line)
|
|
9344
|
+
local matchStart = if usePattern then findFirstPattern(candidate, patternAlternatives, control) else (string.find(candidate, searchPattern, 1, true))
|
|
9345
|
+
if matchStart ~= nil and (maxResultsPerScript == 0 or scriptMatchCount < maxResultsPerScript) then
|
|
9346
|
+
scriptMatchCount += 1
|
|
9347
|
+
totalMatches += 1
|
|
9348
|
+
if totalMatches > maxResults then
|
|
9349
|
+
hitLimit = true
|
|
9350
|
+
break
|
|
9351
|
+
end
|
|
9352
|
+
if not filesOnly then
|
|
9353
|
+
local _object = {
|
|
9354
|
+
line = lineNumber,
|
|
9355
|
+
column = matchStart,
|
|
9356
|
+
text = line,
|
|
9357
|
+
}
|
|
9358
|
+
local _left = "before"
|
|
9359
|
+
local _array = {}
|
|
9360
|
+
local _length_1 = #_array
|
|
9361
|
+
table.move(before, 1, #before, _length_1 + 1, _array)
|
|
9362
|
+
_object[_left] = _array
|
|
9363
|
+
_object.after = {}
|
|
9364
|
+
local lineMatch = _object
|
|
9365
|
+
table.insert(scriptMatches, lineMatch)
|
|
9366
|
+
if contextLines > 0 then
|
|
9367
|
+
local _arg0 = {
|
|
9368
|
+
match = lineMatch,
|
|
9369
|
+
remaining = contextLines,
|
|
9370
|
+
}
|
|
9371
|
+
table.insert(pendingAfter, _arg0)
|
|
9372
|
+
end
|
|
9373
|
+
end
|
|
9374
|
+
end
|
|
9375
|
+
if maxResultsPerScript > 0 and scriptMatchCount >= maxResultsPerScript and #pendingAfter == 0 then
|
|
9376
|
+
break
|
|
9377
|
+
end
|
|
9378
|
+
if contextLines > 0 then
|
|
9379
|
+
table.insert(before, line)
|
|
9380
|
+
while #before > contextLines do
|
|
9381
|
+
table.remove(before, 1)
|
|
9382
|
+
end
|
|
9383
|
+
end
|
|
9384
|
+
lineStart = nextLineStart
|
|
9385
|
+
if nextLineStart == nil then
|
|
9386
|
+
break
|
|
9387
|
+
end
|
|
9388
|
+
lineNumber += 1
|
|
9389
|
+
end
|
|
9390
|
+
end
|
|
9391
|
+
if scriptMatchCount > 0 then
|
|
9392
|
+
local scriptResult = {
|
|
9393
|
+
instancePath = snapshot.instancePath,
|
|
9394
|
+
name = snapshot.name,
|
|
9395
|
+
className = snapshot.className,
|
|
9396
|
+
matches = scriptMatches,
|
|
9397
|
+
}
|
|
9398
|
+
if snapshot.enabled ~= nil then
|
|
9399
|
+
scriptResult.enabled = snapshot.enabled
|
|
9400
|
+
end
|
|
9401
|
+
table.insert(results, scriptResult)
|
|
9402
|
+
end
|
|
9403
|
+
end
|
|
9404
|
+
local children = {}
|
|
9405
|
+
for _, child in corpus:getChildren(instance) do
|
|
9406
|
+
table.insert(children, child)
|
|
9407
|
+
end
|
|
9408
|
+
for index = #children - 1, 0, -1 do
|
|
9409
|
+
local _arg0 = children[index + 1]
|
|
9410
|
+
table.insert(stack, _arg0)
|
|
9411
|
+
end
|
|
9412
|
+
end
|
|
9413
|
+
return {
|
|
9414
|
+
results = results,
|
|
9415
|
+
pattern = pattern,
|
|
9416
|
+
totalMatches = if hitLimit then `>{maxResults}` else totalMatches,
|
|
9417
|
+
scriptsSearched = scriptsSearched,
|
|
9418
|
+
scriptsMatched = #results,
|
|
9419
|
+
truncated = hitLimit,
|
|
9420
|
+
options = {
|
|
9421
|
+
caseSensitive = caseSensitive,
|
|
9422
|
+
contextLines = contextLines,
|
|
9423
|
+
usePattern = usePattern,
|
|
9424
|
+
filesOnly = filesOnly,
|
|
9425
|
+
maxResults = maxResults,
|
|
9426
|
+
maxResultsPerScript = maxResultsPerScript,
|
|
9427
|
+
},
|
|
9428
|
+
}
|
|
9429
|
+
end
|
|
9430
|
+
return {
|
|
9431
|
+
search = search,
|
|
9432
|
+
}
|
|
9433
|
+
end
|
|
9434
|
+
return {
|
|
9435
|
+
createScriptSearch = createScriptSearch,
|
|
9436
|
+
}
|
|
9437
|
+
]]></string>
|
|
9438
|
+
</Properties>
|
|
9439
|
+
</Item>
|
|
9440
|
+
<Item class="ModuleScript" referent="32">
|
|
9242
9441
|
<Properties>
|
|
9243
9442
|
<string name="Name">ServerUrlSettings</string>
|
|
9244
9443
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -9377,11 +9576,11 @@ return {
|
|
|
9377
9576
|
]]></string>
|
|
9378
9577
|
</Properties>
|
|
9379
9578
|
</Item>
|
|
9380
|
-
<Item class="ModuleScript" referent="
|
|
9579
|
+
<Item class="ModuleScript" referent="33">
|
|
9381
9580
|
<Properties>
|
|
9382
9581
|
<string name="Name">State</string>
|
|
9383
9582
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
9384
|
-
local CURRENT_VERSION = "3.0.
|
|
9583
|
+
local CURRENT_VERSION = "3.0.5"
|
|
9385
9584
|
local PLUGIN_VARIANT = "main"
|
|
9386
9585
|
local BASE_PORT = 58741
|
|
9387
9586
|
local function createConnection(port)
|
|
@@ -9411,7 +9610,7 @@ return {
|
|
|
9411
9610
|
]]></string>
|
|
9412
9611
|
</Properties>
|
|
9413
9612
|
</Item>
|
|
9414
|
-
<Item class="ModuleScript" referent="
|
|
9613
|
+
<Item class="ModuleScript" referent="34">
|
|
9415
9614
|
<Properties>
|
|
9416
9615
|
<string name="Name">StopPlayMonitor</string>
|
|
9417
9616
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -9461,6 +9660,7 @@ local WAIT_POLL_SEC = 0.1
|
|
|
9461
9660
|
local REQUEST_TTL_SEC = 12.0
|
|
9462
9661
|
local pluginRef
|
|
9463
9662
|
local endTestIssued = false
|
|
9663
|
+
local transportLifecycle
|
|
9464
9664
|
local function init(p)
|
|
9465
9665
|
pluginRef = p
|
|
9466
9666
|
end
|
|
@@ -9604,15 +9804,29 @@ local function handleStopRequest(key, request)
|
|
|
9604
9804
|
return nil
|
|
9605
9805
|
end
|
|
9606
9806
|
endTestIssued = true
|
|
9807
|
+
local lifecycle = transportLifecycle
|
|
9808
|
+
if lifecycle ~= nil then
|
|
9809
|
+
local closeOk, closeError = pcall(lifecycle.beforeEndTest)
|
|
9810
|
+
if not closeOk then
|
|
9811
|
+
warn(`[robloxstudio-mcp] Failed to close the play-server transport before EndTest: {tostring(closeError)}`)
|
|
9812
|
+
end
|
|
9813
|
+
end
|
|
9607
9814
|
local endOk, endErr = pcall(function()
|
|
9608
9815
|
return StudioTestService:EndTest("stopped_by_mcp")
|
|
9609
9816
|
end)
|
|
9610
9817
|
writeResult(key, request, endOk, if endOk then nil else tostring(endErr))
|
|
9611
9818
|
if not endOk then
|
|
9612
9819
|
endTestIssued = false
|
|
9820
|
+
if lifecycle ~= nil then
|
|
9821
|
+
local restoreOk, restoreError = pcall(lifecycle.afterEndTestFailure)
|
|
9822
|
+
if not restoreOk then
|
|
9823
|
+
warn(`[robloxstudio-mcp] Failed to restore the play-server transport after EndTest failed: {tostring(restoreError)}`)
|
|
9824
|
+
end
|
|
9825
|
+
end
|
|
9613
9826
|
end
|
|
9614
9827
|
end
|
|
9615
|
-
local function startMonitor()
|
|
9828
|
+
local function startMonitor(lifecycle)
|
|
9829
|
+
transportLifecycle = lifecycle
|
|
9616
9830
|
if not pluginRef then
|
|
9617
9831
|
warn("[robloxstudio-mcp] StopPlayMonitor.startMonitor called before init; skipping")
|
|
9618
9832
|
return nil
|
|
@@ -9702,7 +9916,7 @@ return {
|
|
|
9702
9916
|
]]></string>
|
|
9703
9917
|
</Properties>
|
|
9704
9918
|
</Item>
|
|
9705
|
-
<Item class="ModuleScript" referent="
|
|
9919
|
+
<Item class="ModuleScript" referent="35">
|
|
9706
9920
|
<Properties>
|
|
9707
9921
|
<string name="Name">StudioEventStream</string>
|
|
9708
9922
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -9718,12 +9932,13 @@ local MAX_TERMINAL_RESPONSES = 256
|
|
|
9718
9932
|
local STREAM_SILENCE_TIMEOUT_SECONDS = 20
|
|
9719
9933
|
local options
|
|
9720
9934
|
local active = false
|
|
9935
|
+
local shutdownSuspended = false
|
|
9721
9936
|
local generation = 0
|
|
9722
9937
|
local reconnectAttempt = 0
|
|
9723
9938
|
local streamClient
|
|
9724
9939
|
local streamConnections = {}
|
|
9725
9940
|
local lastValidEventAt = 0
|
|
9726
|
-
local
|
|
9941
|
+
local inFlightRequests = {}
|
|
9727
9942
|
local pendingResponses = {}
|
|
9728
9943
|
local terminalResponseIds = {}
|
|
9729
9944
|
local terminalResponseOrder = {}
|
|
@@ -9780,6 +9995,21 @@ local function decodeMessage(message)
|
|
|
9780
9995
|
_object[_left_2] = if type(_pluginVariant) == "string" then envelope.pluginVariant else nil
|
|
9781
9996
|
return _object
|
|
9782
9997
|
end
|
|
9998
|
+
if envelope.kind == "cancel" then
|
|
9999
|
+
local _requestId = envelope.requestId
|
|
10000
|
+
local _condition = not (type(_requestId) == "string")
|
|
10001
|
+
if not _condition then
|
|
10002
|
+
_condition = (envelope.reason ~= "timeout" and envelope.reason ~= "aborted" and envelope.reason ~= "connection_closed")
|
|
10003
|
+
end
|
|
10004
|
+
if _condition then
|
|
10005
|
+
return nil
|
|
10006
|
+
end
|
|
10007
|
+
return {
|
|
10008
|
+
kind = "cancel",
|
|
10009
|
+
requestId = envelope.requestId,
|
|
10010
|
+
reason = envelope.reason,
|
|
10011
|
+
}
|
|
10012
|
+
end
|
|
9783
10013
|
if envelope.kind == "request" then
|
|
9784
10014
|
local _requestId = envelope.requestId
|
|
9785
10015
|
local _condition = not (type(_requestId) == "string")
|
|
@@ -9792,6 +10022,13 @@ local function decodeMessage(message)
|
|
|
9792
10022
|
if not _condition then
|
|
9793
10023
|
local _endpoint = envelope.endpoint
|
|
9794
10024
|
_condition = not (type(_endpoint) == "string")
|
|
10025
|
+
if not _condition then
|
|
10026
|
+
local _remainingMs = envelope.remainingMs
|
|
10027
|
+
_condition = not (type(_remainingMs) == "number")
|
|
10028
|
+
if not _condition then
|
|
10029
|
+
_condition = envelope.remainingMs < 0
|
|
10030
|
+
end
|
|
10031
|
+
end
|
|
9795
10032
|
end
|
|
9796
10033
|
end
|
|
9797
10034
|
end
|
|
@@ -9810,6 +10047,7 @@ local function decodeMessage(message)
|
|
|
9810
10047
|
target = envelope.target,
|
|
9811
10048
|
endpoint = envelope.endpoint,
|
|
9812
10049
|
data = data,
|
|
10050
|
+
remainingMs = envelope.remainingMs,
|
|
9813
10051
|
}
|
|
9814
10052
|
end
|
|
9815
10053
|
return nil
|
|
@@ -9827,6 +10065,21 @@ local function closeCurrentStream()
|
|
|
9827
10065
|
end)
|
|
9828
10066
|
end
|
|
9829
10067
|
end
|
|
10068
|
+
local function disconnectSession(currentOptions)
|
|
10069
|
+
pcall(function()
|
|
10070
|
+
return HttpService:RequestAsync({
|
|
10071
|
+
Url = `{currentOptions.serverUrl}/disconnect`,
|
|
10072
|
+
Method = "POST",
|
|
10073
|
+
Headers = {
|
|
10074
|
+
["Content-Type"] = "application/json",
|
|
10075
|
+
},
|
|
10076
|
+
Body = HttpService:JSONEncode({
|
|
10077
|
+
pluginSessionId = PluginSession.id,
|
|
10078
|
+
timestamp = tick(),
|
|
10079
|
+
}),
|
|
10080
|
+
})
|
|
10081
|
+
end)
|
|
10082
|
+
end
|
|
9830
10083
|
local function responseRetryDelay(attempt)
|
|
9831
10084
|
return math.min(INITIAL_RESPONSE_RETRY_DELAY_SECONDS * math.pow(2, math.max(attempt - 1, 0)), MAX_RESPONSE_RETRY_DELAY_SECONDS)
|
|
9832
10085
|
end
|
|
@@ -9977,6 +10230,21 @@ local function encodeResponse(requestId, response)
|
|
|
9977
10230
|
error = `Plugin response serialization failed: {tostring(encoded)}`,
|
|
9978
10231
|
})
|
|
9979
10232
|
end
|
|
10233
|
+
local function cancelRequest(event)
|
|
10234
|
+
local _requestId = event.requestId
|
|
10235
|
+
local inFlight = inFlightRequests[_requestId]
|
|
10236
|
+
if inFlight ~= nil then
|
|
10237
|
+
inFlight.cancelled = true
|
|
10238
|
+
end
|
|
10239
|
+
local _requestId_1 = event.requestId
|
|
10240
|
+
local pending = pendingResponses[_requestId_1]
|
|
10241
|
+
if pending ~= nil then
|
|
10242
|
+
pending.retryToken += 1
|
|
10243
|
+
local _requestId_2 = event.requestId
|
|
10244
|
+
pendingResponses[_requestId_2] = nil
|
|
10245
|
+
end
|
|
10246
|
+
rememberTerminalResponse(event.requestId)
|
|
10247
|
+
end
|
|
9980
10248
|
local function dispatchRequest(request)
|
|
9981
10249
|
local _requestId = request.requestId
|
|
9982
10250
|
local _condition = terminalResponseIds[_requestId] ~= nil
|
|
@@ -9985,7 +10253,7 @@ local function dispatchRequest(request)
|
|
|
9985
10253
|
_condition = pendingResponses[_requestId_1] ~= nil
|
|
9986
10254
|
if not _condition then
|
|
9987
10255
|
local _requestId_2 = request.requestId
|
|
9988
|
-
_condition =
|
|
10256
|
+
_condition = inFlightRequests[_requestId_2] ~= nil
|
|
9989
10257
|
end
|
|
9990
10258
|
end
|
|
9991
10259
|
if _condition then
|
|
@@ -9995,12 +10263,48 @@ local function dispatchRequest(request)
|
|
|
9995
10263
|
if not active or dispatchOptions == nil then
|
|
9996
10264
|
return nil
|
|
9997
10265
|
end
|
|
10266
|
+
local inFlight = {
|
|
10267
|
+
cancelled = false,
|
|
10268
|
+
}
|
|
10269
|
+
local context = {
|
|
10270
|
+
requestId = request.requestId,
|
|
10271
|
+
deadlineAt = os.clock() + request.remainingMs / 1000,
|
|
10272
|
+
isCancelled = function()
|
|
10273
|
+
return inFlight.cancelled
|
|
10274
|
+
end,
|
|
10275
|
+
}
|
|
9998
10276
|
local _requestId_1 = request.requestId
|
|
9999
|
-
|
|
10277
|
+
inFlightRequests[_requestId_1] = inFlight
|
|
10000
10278
|
task.spawn(function()
|
|
10279
|
+
local _condition_1 = inFlight.cancelled
|
|
10280
|
+
if not _condition_1 then
|
|
10281
|
+
local _requestId_2 = request.requestId
|
|
10282
|
+
_condition_1 = terminalResponseIds[_requestId_2] ~= nil
|
|
10283
|
+
end
|
|
10284
|
+
if _condition_1 then
|
|
10285
|
+
local _requestId_2 = request.requestId
|
|
10286
|
+
if inFlightRequests[_requestId_2] == inFlight then
|
|
10287
|
+
local _requestId_3 = request.requestId
|
|
10288
|
+
inFlightRequests[_requestId_3] = nil
|
|
10289
|
+
end
|
|
10290
|
+
return nil
|
|
10291
|
+
end
|
|
10001
10292
|
local dispatchOk, response = pcall(function()
|
|
10002
|
-
return dispatchOptions.dispatchRequest(request)
|
|
10293
|
+
return dispatchOptions.dispatchRequest(request, context)
|
|
10003
10294
|
end)
|
|
10295
|
+
local _condition_2 = inFlight.cancelled
|
|
10296
|
+
if not _condition_2 then
|
|
10297
|
+
local _requestId_2 = request.requestId
|
|
10298
|
+
_condition_2 = terminalResponseIds[_requestId_2] ~= nil
|
|
10299
|
+
end
|
|
10300
|
+
if _condition_2 then
|
|
10301
|
+
local _requestId_2 = request.requestId
|
|
10302
|
+
if inFlightRequests[_requestId_2] == inFlight then
|
|
10303
|
+
local _requestId_3 = request.requestId
|
|
10304
|
+
inFlightRequests[_requestId_3] = nil
|
|
10305
|
+
end
|
|
10306
|
+
return nil
|
|
10307
|
+
end
|
|
10004
10308
|
local responseData = if dispatchOk then response else {
|
|
10005
10309
|
error = tostring(response),
|
|
10006
10310
|
}
|
|
@@ -10013,7 +10317,7 @@ local function dispatchRequest(request)
|
|
|
10013
10317
|
local _requestId_2 = request.requestId
|
|
10014
10318
|
pendingResponses[_requestId_2] = entry
|
|
10015
10319
|
local _requestId_3 = request.requestId
|
|
10016
|
-
|
|
10320
|
+
inFlightRequests[_requestId_3] = nil
|
|
10017
10321
|
postPendingResponse(request.requestId, entry)
|
|
10018
10322
|
end)
|
|
10019
10323
|
end
|
|
@@ -10232,6 +10536,10 @@ function connect(expectedGeneration)
|
|
|
10232
10536
|
end)
|
|
10233
10537
|
return nil
|
|
10234
10538
|
end
|
|
10539
|
+
if event.kind == "cancel" then
|
|
10540
|
+
cancelRequest(event)
|
|
10541
|
+
return nil
|
|
10542
|
+
end
|
|
10235
10543
|
if event.kind == "request" then
|
|
10236
10544
|
dispatchRequest(event)
|
|
10237
10545
|
return nil
|
|
@@ -10260,11 +10568,12 @@ function connect(expectedGeneration)
|
|
|
10260
10568
|
end
|
|
10261
10569
|
local stop
|
|
10262
10570
|
local function start(newOptions)
|
|
10263
|
-
if active then
|
|
10571
|
+
if active or shutdownSuspended then
|
|
10264
10572
|
stop()
|
|
10265
10573
|
end
|
|
10266
10574
|
options = newOptions
|
|
10267
10575
|
active = true
|
|
10576
|
+
shutdownSuspended = false
|
|
10268
10577
|
reconnectAttempt = 0
|
|
10269
10578
|
generation += 1
|
|
10270
10579
|
connect(generation)
|
|
@@ -10278,42 +10587,64 @@ function refresh()
|
|
|
10278
10587
|
reconnectAttempt = 0
|
|
10279
10588
|
connect(generation)
|
|
10280
10589
|
end
|
|
10590
|
+
-- EndTest can tear down a play DataModel without giving Plugin.Unloading a
|
|
10591
|
+
-- usable execution window. Release the native WebStreamClient before the
|
|
10592
|
+
-- yielding unregister request, but retain local request state and connection
|
|
10593
|
+
-- options so a failed EndTest can register again and reconnect.
|
|
10594
|
+
local function suspendForShutdown()
|
|
10595
|
+
local currentOptions = options
|
|
10596
|
+
if not active or currentOptions == nil then
|
|
10597
|
+
return nil
|
|
10598
|
+
end
|
|
10599
|
+
active = false
|
|
10600
|
+
shutdownSuspended = true
|
|
10601
|
+
generation += 1
|
|
10602
|
+
reconnectAttempt = 0
|
|
10603
|
+
closeCurrentStream()
|
|
10604
|
+
disconnectSession(currentOptions)
|
|
10605
|
+
end
|
|
10606
|
+
local function resumeAfterShutdownFailure()
|
|
10607
|
+
if not shutdownSuspended or options == nil then
|
|
10608
|
+
return nil
|
|
10609
|
+
end
|
|
10610
|
+
shutdownSuspended = false
|
|
10611
|
+
active = true
|
|
10612
|
+
generation += 1
|
|
10613
|
+
reconnectAttempt = 0
|
|
10614
|
+
connect(generation)
|
|
10615
|
+
end
|
|
10281
10616
|
function stop()
|
|
10282
|
-
if not active then
|
|
10617
|
+
if not active and not shutdownSuspended then
|
|
10283
10618
|
return nil
|
|
10284
10619
|
end
|
|
10285
10620
|
local currentOptions = options
|
|
10286
10621
|
active = false
|
|
10622
|
+
shutdownSuspended = false
|
|
10287
10623
|
generation += 1
|
|
10624
|
+
for _, inFlight in inFlightRequests do
|
|
10625
|
+
inFlight.cancelled = true
|
|
10626
|
+
end
|
|
10627
|
+
table.clear(inFlightRequests)
|
|
10628
|
+
table.clear(pendingResponses)
|
|
10288
10629
|
closeCurrentStream()
|
|
10289
10630
|
table.clear(readyFailureLogKeys)
|
|
10290
10631
|
options = nil
|
|
10291
10632
|
reconnectAttempt = 0
|
|
10292
10633
|
if currentOptions ~= nil then
|
|
10293
|
-
|
|
10294
|
-
return HttpService:RequestAsync({
|
|
10295
|
-
Url = `{currentOptions.serverUrl}/disconnect`,
|
|
10296
|
-
Method = "POST",
|
|
10297
|
-
Headers = {
|
|
10298
|
-
["Content-Type"] = "application/json",
|
|
10299
|
-
},
|
|
10300
|
-
Body = HttpService:JSONEncode({
|
|
10301
|
-
pluginSessionId = PluginSession.id,
|
|
10302
|
-
timestamp = tick(),
|
|
10303
|
-
}),
|
|
10304
|
-
})
|
|
10305
|
-
end)
|
|
10634
|
+
disconnectSession(currentOptions)
|
|
10306
10635
|
end
|
|
10307
10636
|
end
|
|
10308
10637
|
return {
|
|
10309
10638
|
start = start,
|
|
10310
10639
|
refresh = refresh,
|
|
10640
|
+
suspendForShutdown = suspendForShutdown,
|
|
10641
|
+
resumeAfterShutdownFailure = resumeAfterShutdownFailure,
|
|
10311
10642
|
stop = stop,
|
|
10312
10643
|
}
|
|
10313
10644
|
]]></string>
|
|
10314
10645
|
</Properties>
|
|
10315
10646
|
</Item>
|
|
10316
|
-
<Item class="ModuleScript" referent="
|
|
10647
|
+
<Item class="ModuleScript" referent="36">
|
|
10317
10648
|
<Properties>
|
|
10318
10649
|
<string name="Name">UI</string>
|
|
10319
10650
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -10865,7 +11196,7 @@ return {
|
|
|
10865
11196
|
]]></string>
|
|
10866
11197
|
</Properties>
|
|
10867
11198
|
</Item>
|
|
10868
|
-
<Item class="ModuleScript" referent="
|
|
11199
|
+
<Item class="ModuleScript" referent="37">
|
|
10869
11200
|
<Properties>
|
|
10870
11201
|
<string name="Name">Utils</string>
|
|
10871
11202
|
<string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
|
|
@@ -11149,21 +11480,11 @@ local function splitLines(source)
|
|
|
11149
11480
|
end
|
|
11150
11481
|
local normalized = (string.gsub((string.gsub(_condition, "\r\n", "\n")), "\r", "\n"))
|
|
11151
11482
|
local endsWithNewline = string.sub(normalized, -1) == "\n"
|
|
11152
|
-
local lines =
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
local _arg0 = string.sub(normalized, start, newlinePos - 1)
|
|
11158
|
-
table.insert(lines, _arg0)
|
|
11159
|
-
start = newlinePos + 1
|
|
11160
|
-
else
|
|
11161
|
-
local remainder = string.sub(normalized, start)
|
|
11162
|
-
if remainder ~= "" or not endsWithNewline then
|
|
11163
|
-
table.insert(lines, remainder)
|
|
11164
|
-
end
|
|
11165
|
-
break
|
|
11166
|
-
end
|
|
11483
|
+
local lines = string.split(normalized, "\n")
|
|
11484
|
+
-- split() includes a final empty field for a trailing newline. Keep the
|
|
11485
|
+
-- existing splitLines contract, which tracks that newline separately.
|
|
11486
|
+
if endsWithNewline and lines[#lines] == "" then
|
|
11487
|
+
lines[#lines] = nil
|
|
11167
11488
|
end
|
|
11168
11489
|
if #lines == 0 then
|
|
11169
11490
|
table.insert(lines, "")
|
|
@@ -11178,15 +11499,21 @@ local function joinLines(lines, hadTrailingNewline)
|
|
|
11178
11499
|
return source
|
|
11179
11500
|
end
|
|
11180
11501
|
local function readScriptSource(instance)
|
|
11181
|
-
local
|
|
11502
|
+
local editorOk, editorSource = pcall(function()
|
|
11503
|
+
return ScriptEditorService:GetEditorSource(instance)
|
|
11504
|
+
end)
|
|
11505
|
+
if editorOk then
|
|
11506
|
+
return editorSource
|
|
11507
|
+
end
|
|
11508
|
+
local documentOk, documentSource = pcall(function()
|
|
11182
11509
|
local doc = ScriptEditorService:FindScriptDocument(instance)
|
|
11183
11510
|
if doc then
|
|
11184
11511
|
return doc:GetText()
|
|
11185
11512
|
end
|
|
11186
11513
|
return nil
|
|
11187
11514
|
end)
|
|
11188
|
-
if
|
|
11189
|
-
return
|
|
11515
|
+
if documentOk and documentSource ~= nil then
|
|
11516
|
+
return documentSource
|
|
11190
11517
|
end
|
|
11191
11518
|
-- @rbxts/types does not expose PluginSecurity Source reads.
|
|
11192
11519
|
local readableScript = instance
|
|
@@ -11710,11 +12037,11 @@ return {
|
|
|
11710
12037
|
</Properties>
|
|
11711
12038
|
</Item>
|
|
11712
12039
|
</Item>
|
|
11713
|
-
<Item class="Folder" referent="
|
|
12040
|
+
<Item class="Folder" referent="42">
|
|
11714
12041
|
<Properties>
|
|
11715
12042
|
<string name="Name">include</string>
|
|
11716
12043
|
</Properties>
|
|
11717
|
-
<Item class="ModuleScript" referent="
|
|
12044
|
+
<Item class="ModuleScript" referent="38">
|
|
11718
12045
|
<Properties>
|
|
11719
12046
|
<string name="Name">LibMP</string>
|
|
11720
12047
|
<string name="Source"><![CDATA[-- =============================================================================
|
|
@@ -168098,7 +168425,7 @@ return LibMP
|
|
|
168098
168425
|
]]></string>
|
|
168099
168426
|
</Properties>
|
|
168100
168427
|
</Item>
|
|
168101
|
-
<Item class="ModuleScript" referent="
|
|
168428
|
+
<Item class="ModuleScript" referent="39">
|
|
168102
168429
|
<Properties>
|
|
168103
168430
|
<string name="Name">Promise</string>
|
|
168104
168431
|
<string name="Source"><![CDATA[--[[
|
|
@@ -170172,7 +170499,7 @@ return Promise
|
|
|
170172
170499
|
]]></string>
|
|
170173
170500
|
</Properties>
|
|
170174
170501
|
</Item>
|
|
170175
|
-
<Item class="ModuleScript" referent="
|
|
170502
|
+
<Item class="ModuleScript" referent="40">
|
|
170176
170503
|
<Properties>
|
|
170177
170504
|
<string name="Name">RuntimeLib</string>
|
|
170178
170505
|
<string name="Source"><![CDATA[local Promise = require(script.Parent.Promise)
|
|
@@ -170439,15 +170766,15 @@ return TS
|
|
|
170439
170766
|
</Properties>
|
|
170440
170767
|
</Item>
|
|
170441
170768
|
</Item>
|
|
170442
|
-
<Item class="Folder" referent="
|
|
170769
|
+
<Item class="Folder" referent="43">
|
|
170443
170770
|
<Properties>
|
|
170444
170771
|
<string name="Name">node_modules</string>
|
|
170445
170772
|
</Properties>
|
|
170446
|
-
<Item class="Folder" referent="
|
|
170773
|
+
<Item class="Folder" referent="44">
|
|
170447
170774
|
<Properties>
|
|
170448
170775
|
<string name="Name">@rbxts</string>
|
|
170449
170776
|
</Properties>
|
|
170450
|
-
<Item class="ModuleScript" referent="
|
|
170777
|
+
<Item class="ModuleScript" referent="41">
|
|
170451
170778
|
<Properties>
|
|
170452
170779
|
<string name="Name">services</string>
|
|
170453
170780
|
<string name="Source"><![CDATA[return setmetatable({}, {
|