dominus-cli 2.0.0 → 2.1.1
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/CHANGELOG.md +195 -0
- package/README.md +162 -248
- package/dist/install-plugin.js +75 -0
- package/dist/install-plugin.js.map +1 -0
- package/dist/mcp.js +1394 -4764
- package/dist/mcp.js.map +1 -1
- package/docs/DOMINUS_2_PLAN.md +75 -0
- package/package.json +27 -26
- package/plugin/Dominus.rbxmx +3674 -0
- package/plugin/src/BridgeConfig.lua +2 -0
- package/plugin/src/CommandRouter.lua +94 -0
- package/plugin/src/Explorer.lua +530 -373
- package/plugin/src/InstanceRegistry.lua +122 -0
- package/plugin/src/Mutation.lua +135 -0
- package/plugin/src/Properties.lua +859 -785
- package/plugin/src/Protocol.lua +30 -30
- package/plugin/src/Reflection.lua +91 -59
- package/plugin/src/TestRunner.lua +69 -141
- package/plugin/src/UIBuilder.lua +857 -727
- package/plugin/src/ValueCodec.lua +231 -0
- package/plugin/src/Watcher.lua +85 -85
- package/plugin/src/WsClient.lua +166 -81
- package/plugin/src/init.server.lua +191 -365
- package/dist/index.js +0 -7998
- package/dist/index.js.map +0 -1
- package/plugin/Dominus.rbxm +0 -0
- package/plugin/src/Executor.lua +0 -117
package/plugin/src/Protocol.lua
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
--[[
|
|
2
|
-
Message protocol: JSON encoding/decoding with unique ID generation
|
|
3
|
-
]]
|
|
4
|
-
|
|
5
|
-
local HttpService = game:GetService("HttpService")
|
|
6
|
-
|
|
7
|
-
local Protocol = {}
|
|
8
|
-
|
|
9
|
-
function Protocol.encode(data)
|
|
10
|
-
return HttpService:JSONEncode(data)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
function Protocol.decode(json)
|
|
14
|
-
return HttpService:JSONDecode(json)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
function Protocol.generateId()
|
|
18
|
-
return HttpService:GenerateGUID(false)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
function Protocol.createMessage(msgType, payload)
|
|
22
|
-
return {
|
|
23
|
-
id = Protocol.generateId(),
|
|
24
|
-
type = msgType,
|
|
25
|
-
payload = payload,
|
|
26
|
-
ts = os.clock(),
|
|
27
|
-
}
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
return Protocol
|
|
1
|
+
--[[
|
|
2
|
+
Message protocol: JSON encoding/decoding with unique ID generation
|
|
3
|
+
]]
|
|
4
|
+
|
|
5
|
+
local HttpService = game:GetService("HttpService")
|
|
6
|
+
|
|
7
|
+
local Protocol = {}
|
|
8
|
+
|
|
9
|
+
function Protocol.encode(data)
|
|
10
|
+
return HttpService:JSONEncode(data)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
function Protocol.decode(json)
|
|
14
|
+
return HttpService:JSONDecode(json)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
function Protocol.generateId()
|
|
18
|
+
return HttpService:GenerateGUID(false)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
function Protocol.createMessage(msgType, payload)
|
|
22
|
+
return {
|
|
23
|
+
id = Protocol.generateId(),
|
|
24
|
+
type = msgType,
|
|
25
|
+
payload = payload,
|
|
26
|
+
ts = os.clock(),
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
return Protocol
|
|
@@ -1,22 +1,39 @@
|
|
|
1
|
-
--[[
|
|
2
|
-
Reflection module: query Roblox class/property/method/event information
|
|
3
|
-
Uses the new ReflectionService API (2025+):
|
|
4
|
-
- GetClass(className, filter?)
|
|
5
|
-
- GetClasses(filter?)
|
|
6
|
-
- GetPropertiesOfClass(className, filter?)
|
|
7
|
-
- GetMethodsOfClass(className, filter?)
|
|
8
|
-
- GetEventsOfClass(className, filter?)
|
|
9
|
-
]]
|
|
10
|
-
|
|
11
1
|
local ReflectionService = game:GetService("ReflectionService")
|
|
2
|
+
|
|
12
3
|
local Reflection = {}
|
|
13
4
|
|
|
14
|
-
local function safeCall(
|
|
15
|
-
local ok, result = pcall(
|
|
16
|
-
if ok then
|
|
5
|
+
local function safeCall(callback)
|
|
6
|
+
local ok, result = pcall(callback)
|
|
7
|
+
if ok then
|
|
8
|
+
return result
|
|
9
|
+
end
|
|
17
10
|
return nil, result
|
|
18
11
|
end
|
|
19
12
|
|
|
13
|
+
local function typeName(reflectionType)
|
|
14
|
+
if reflectionType == nil then
|
|
15
|
+
return "unknown"
|
|
16
|
+
end
|
|
17
|
+
if type(reflectionType) == "table" then
|
|
18
|
+
return reflectionType.Name or reflectionType.name or reflectionType.Type or tostring(reflectionType)
|
|
19
|
+
end
|
|
20
|
+
return tostring(reflectionType)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
local function displayCategory(member)
|
|
24
|
+
if member.Display and member.Display.Category then
|
|
25
|
+
return member.Display.Category
|
|
26
|
+
end
|
|
27
|
+
return "Other"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
local function permitString(permits, key)
|
|
31
|
+
if not permits or permits[key] == nil then
|
|
32
|
+
return nil
|
|
33
|
+
end
|
|
34
|
+
return tostring(permits[key])
|
|
35
|
+
end
|
|
36
|
+
|
|
20
37
|
function Reflection.getClassInfo(className)
|
|
21
38
|
local classData, classErr = safeCall(function()
|
|
22
39
|
return ReflectionService:GetClass(className)
|
|
@@ -27,13 +44,15 @@ function Reflection.getClassInfo(className)
|
|
|
27
44
|
return ReflectionService:GetPropertiesOfClass(className)
|
|
28
45
|
end)
|
|
29
46
|
if propData then
|
|
30
|
-
for _,
|
|
47
|
+
for _, property in propData do
|
|
31
48
|
table.insert(properties, {
|
|
32
|
-
name =
|
|
33
|
-
type =
|
|
34
|
-
category =
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
name = property.Name,
|
|
50
|
+
type = typeName(property.Type),
|
|
51
|
+
category = displayCategory(property),
|
|
52
|
+
owner = property.Owner,
|
|
53
|
+
serialized = property.Serialized,
|
|
54
|
+
readPermission = permitString(property.Permits, "Read"),
|
|
55
|
+
writePermission = permitString(property.Permits, "Write"),
|
|
37
56
|
})
|
|
38
57
|
end
|
|
39
58
|
end
|
|
@@ -44,18 +63,19 @@ function Reflection.getClassInfo(className)
|
|
|
44
63
|
end)
|
|
45
64
|
if methodData then
|
|
46
65
|
for _, method in methodData do
|
|
47
|
-
local
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
end
|
|
66
|
+
local parameters = {}
|
|
67
|
+
for _, parameter in method.Parameters or {} do
|
|
68
|
+
table.insert(parameters, {
|
|
69
|
+
name = parameter.Name,
|
|
70
|
+
type = typeName(parameter.Type),
|
|
71
|
+
defaultValue = parameter.DefaultValue,
|
|
72
|
+
})
|
|
55
73
|
end
|
|
56
74
|
table.insert(methods, {
|
|
57
75
|
name = method.Name,
|
|
58
|
-
parameters =
|
|
76
|
+
parameters = parameters,
|
|
77
|
+
returnType = typeName(method.ReturnType),
|
|
78
|
+
canYield = method.CanYield,
|
|
59
79
|
owner = method.Owner,
|
|
60
80
|
})
|
|
61
81
|
end
|
|
@@ -67,70 +87,82 @@ function Reflection.getClassInfo(className)
|
|
|
67
87
|
end)
|
|
68
88
|
if eventData then
|
|
69
89
|
for _, event in eventData do
|
|
70
|
-
local
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
table.insert(params, {
|
|
74
|
-
name = param.Name,
|
|
75
|
-
type = param.Type and tostring(param.Type) or "unknown",
|
|
76
|
-
})
|
|
77
|
-
end
|
|
90
|
+
local parameters = {}
|
|
91
|
+
for _, parameter in event.Parameters or {} do
|
|
92
|
+
table.insert(parameters, { name = parameter.Name, type = typeName(parameter.Type) })
|
|
78
93
|
end
|
|
79
|
-
table.insert(events, {
|
|
80
|
-
name = event.Name,
|
|
81
|
-
parameters = params,
|
|
82
|
-
owner = event.Owner,
|
|
83
|
-
})
|
|
94
|
+
table.insert(events, { name = event.Name, parameters = parameters, owner = event.Owner })
|
|
84
95
|
end
|
|
85
96
|
end
|
|
86
97
|
|
|
87
98
|
local result = {
|
|
99
|
+
success = classData ~= nil or #properties > 0,
|
|
88
100
|
className = className,
|
|
89
101
|
properties = properties,
|
|
90
102
|
methods = methods,
|
|
91
103
|
events = events,
|
|
92
104
|
}
|
|
93
|
-
|
|
94
105
|
if classData then
|
|
95
|
-
result.superclass = classData.Superclass
|
|
96
|
-
result.
|
|
97
|
-
result.
|
|
106
|
+
result.superclass = classData.Superclass and tostring(classData.Superclass) or nil
|
|
107
|
+
result.newPermission = permitString(classData.Permits, "New")
|
|
108
|
+
result.creatable = classData.Permits ~= nil and classData.Permits.New ~= nil
|
|
98
109
|
end
|
|
99
|
-
|
|
100
110
|
if classErr and #properties == 0 then
|
|
101
111
|
result.error = "Could not reflect class: " .. tostring(classErr)
|
|
102
112
|
end
|
|
103
|
-
|
|
104
113
|
return result
|
|
105
114
|
end
|
|
106
115
|
|
|
107
116
|
function Reflection.getPropertyTypes(className)
|
|
108
|
-
local
|
|
117
|
+
local properties = safeCall(function()
|
|
109
118
|
return ReflectionService:GetPropertiesOfClass(className)
|
|
110
119
|
end)
|
|
111
|
-
if not
|
|
120
|
+
if not properties then
|
|
121
|
+
return {}
|
|
122
|
+
end
|
|
123
|
+
local result = {}
|
|
124
|
+
for _, property in properties do
|
|
125
|
+
result[property.Name] = typeName(property.Type)
|
|
126
|
+
end
|
|
127
|
+
return result
|
|
128
|
+
end
|
|
112
129
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
function Reflection.getPropertyMetadata(className)
|
|
131
|
+
local properties = safeCall(function()
|
|
132
|
+
return ReflectionService:GetPropertiesOfClass(className)
|
|
133
|
+
end)
|
|
134
|
+
if not properties then
|
|
135
|
+
return {}
|
|
136
|
+
end
|
|
137
|
+
local result = {}
|
|
138
|
+
for _, property in properties do
|
|
139
|
+
result[property.Name] = {
|
|
140
|
+
type = typeName(property.Type),
|
|
141
|
+
category = displayCategory(property),
|
|
142
|
+
writable = property.Permits == nil or property.Permits.Write ~= nil,
|
|
143
|
+
}
|
|
116
144
|
end
|
|
117
|
-
return
|
|
145
|
+
return result
|
|
118
146
|
end
|
|
119
147
|
|
|
120
148
|
function Reflection.listClasses()
|
|
121
149
|
local classes = safeCall(function()
|
|
122
150
|
return ReflectionService:GetClasses()
|
|
123
151
|
end)
|
|
124
|
-
if not classes then
|
|
125
|
-
|
|
152
|
+
if not classes then
|
|
153
|
+
return {}
|
|
154
|
+
end
|
|
126
155
|
local result = {}
|
|
127
|
-
for _,
|
|
156
|
+
for _, classData in classes do
|
|
128
157
|
table.insert(result, {
|
|
129
|
-
name =
|
|
130
|
-
superclass =
|
|
131
|
-
creatable =
|
|
158
|
+
name = classData.Name,
|
|
159
|
+
superclass = classData.Superclass and tostring(classData.Superclass) or nil,
|
|
160
|
+
creatable = classData.Permits ~= nil and classData.Permits.New ~= nil,
|
|
132
161
|
})
|
|
133
162
|
end
|
|
163
|
+
table.sort(result, function(a, b)
|
|
164
|
+
return a.name < b.name
|
|
165
|
+
end)
|
|
134
166
|
return result
|
|
135
167
|
end
|
|
136
168
|
|
|
@@ -1,141 +1,69 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
local
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
success = true,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
function TestRunner.run(testName)
|
|
73
|
-
local results = {}
|
|
74
|
-
|
|
75
|
-
local ok, err = pcall(function()
|
|
76
|
-
local TestService = game:GetService("TestService")
|
|
77
|
-
|
|
78
|
-
if testName then
|
|
79
|
-
local testScript = nil
|
|
80
|
-
for _, desc in game:GetDescendants() do
|
|
81
|
-
if desc:IsA("ModuleScript") and desc.Name == testName then
|
|
82
|
-
testScript = desc
|
|
83
|
-
break
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
if not testScript then
|
|
88
|
-
table.insert(results, {
|
|
89
|
-
name = testName,
|
|
90
|
-
passed = false,
|
|
91
|
-
duration = 0,
|
|
92
|
-
error = "Test not found: " .. testName,
|
|
93
|
-
})
|
|
94
|
-
return
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
local startTime = os.clock()
|
|
98
|
-
local testOk, testErr = pcall(function()
|
|
99
|
-
require(testScript)
|
|
100
|
-
end)
|
|
101
|
-
local duration = os.clock() - startTime
|
|
102
|
-
|
|
103
|
-
table.insert(results, {
|
|
104
|
-
name = testName,
|
|
105
|
-
passed = testOk,
|
|
106
|
-
duration = duration,
|
|
107
|
-
error = not testOk and tostring(testErr) or nil,
|
|
108
|
-
})
|
|
109
|
-
else
|
|
110
|
-
for _, desc in game:GetDescendants() do
|
|
111
|
-
if desc:IsA("ModuleScript") then
|
|
112
|
-
local name = desc.Name
|
|
113
|
-
if string.match(name, "%.test$") or string.match(name, "%.spec$")
|
|
114
|
-
or string.match(name, "Test$") or string.match(name, "Spec$") then
|
|
115
|
-
|
|
116
|
-
local startTime = os.clock()
|
|
117
|
-
local testOk, testErr = pcall(function()
|
|
118
|
-
require(desc)
|
|
119
|
-
end)
|
|
120
|
-
local duration = os.clock() - startTime
|
|
121
|
-
|
|
122
|
-
table.insert(results, {
|
|
123
|
-
name = name,
|
|
124
|
-
passed = testOk,
|
|
125
|
-
duration = duration,
|
|
126
|
-
error = not testOk and tostring(testErr) or nil,
|
|
127
|
-
})
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end)
|
|
133
|
-
|
|
134
|
-
if not ok then
|
|
135
|
-
return { results = {{ name = "TestRunner", passed = false, duration = 0, error = tostring(err) }} }
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
return { results = results }
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
return TestRunner
|
|
1
|
+
local StudioTestService = game:GetService("StudioTestService")
|
|
2
|
+
|
|
3
|
+
local TestRunner = {}
|
|
4
|
+
local activeRunId = nil
|
|
5
|
+
|
|
6
|
+
function TestRunner.executeV2(spec)
|
|
7
|
+
if activeRunId then
|
|
8
|
+
return { success = false, error = "A Dominus Studio test is already running" }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
local mode = spec.mode or "run"
|
|
12
|
+
if mode ~= "run" and mode ~= "play" and mode ~= "multiplayer" then
|
|
13
|
+
return { success = false, error = "mode must be run, play, or multiplayer" }
|
|
14
|
+
end
|
|
15
|
+
local timeoutSeconds = math.clamp((spec.timeoutMs or 120000) / 1000, 5, 600)
|
|
16
|
+
local players = math.clamp(spec.players or 1, 1, 8)
|
|
17
|
+
local runId = tostring(os.clock()) .. ":" .. mode
|
|
18
|
+
activeRunId = runId
|
|
19
|
+
local timedOut = false
|
|
20
|
+
|
|
21
|
+
task.delay(timeoutSeconds, function()
|
|
22
|
+
if activeRunId ~= runId then
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
timedOut = true
|
|
26
|
+
pcall(function()
|
|
27
|
+
if StudioTestService:CanLeaveTest() then
|
|
28
|
+
StudioTestService:LeaveTest()
|
|
29
|
+
end
|
|
30
|
+
end)
|
|
31
|
+
end)
|
|
32
|
+
|
|
33
|
+
local startedAt = os.clock()
|
|
34
|
+
local ok, result = pcall(function()
|
|
35
|
+
if mode == "run" then
|
|
36
|
+
return StudioTestService:ExecuteRunModeAsync(spec.args)
|
|
37
|
+
elseif mode == "play" then
|
|
38
|
+
return StudioTestService:ExecutePlayModeAsync(spec.args)
|
|
39
|
+
end
|
|
40
|
+
return StudioTestService:ExecuteMultiplayerTestAsync(players, spec.args)
|
|
41
|
+
end)
|
|
42
|
+
local duration = os.clock() - startedAt
|
|
43
|
+
if activeRunId == runId then
|
|
44
|
+
activeRunId = nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if timedOut then
|
|
48
|
+
return { success = false, timedOut = true, error = "Studio test exceeded its timeout", duration = duration }
|
|
49
|
+
end
|
|
50
|
+
if not ok then
|
|
51
|
+
return { success = false, error = tostring(result), duration = duration }
|
|
52
|
+
end
|
|
53
|
+
return {
|
|
54
|
+
success = true,
|
|
55
|
+
mode = mode,
|
|
56
|
+
players = mode == "multiplayer" and players or nil,
|
|
57
|
+
result = result,
|
|
58
|
+
duration = duration,
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
function TestRunner.endTest(value)
|
|
63
|
+
local ok, err = pcall(function()
|
|
64
|
+
StudioTestService:EndTest(value)
|
|
65
|
+
end)
|
|
66
|
+
return ok and { success = true } or { success = false, error = tostring(err) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
return TestRunner
|