@el4cteo/rbx-studio-mcp 0.6.1 → 0.6.7

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.
Files changed (75) hide show
  1. package/README.md +28 -2
  2. package/dist/bridge/console.js +182 -0
  3. package/dist/bridge/console.js.map +1 -1
  4. package/dist/index.js +8 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/cloudassets.js +233 -0
  7. package/dist/lib/cloudassets.js.map +1 -0
  8. package/dist/lib/credentials.js +180 -0
  9. package/dist/lib/credentials.js.map +1 -0
  10. package/dist/lib/livedata.js +325 -0
  11. package/dist/lib/livedata.js.map +1 -0
  12. package/dist/lib/liveluau.js +83 -0
  13. package/dist/lib/liveluau.js.map +1 -0
  14. package/dist/lib/liveops.js +358 -0
  15. package/dist/lib/liveops.js.map +1 -0
  16. package/dist/lib/opencloud.js +235 -0
  17. package/dist/lib/opencloud.js.map +1 -0
  18. package/dist/tools/anim.js +159 -0
  19. package/dist/tools/anim.js.map +1 -0
  20. package/dist/tools/audio.js +96 -0
  21. package/dist/tools/audio.js.map +1 -0
  22. package/dist/tools/character.js +95 -5
  23. package/dist/tools/character.js.map +1 -1
  24. package/dist/tools/data.js +292 -0
  25. package/dist/tools/data.js.map +1 -0
  26. package/dist/tools/device.js +77 -7
  27. package/dist/tools/device.js.map +1 -1
  28. package/dist/tools/discover.js +80 -4
  29. package/dist/tools/discover.js.map +1 -1
  30. package/dist/tools/exec.js +96 -2
  31. package/dist/tools/exec.js.map +1 -1
  32. package/dist/tools/input.js +35 -9
  33. package/dist/tools/input.js.map +1 -1
  34. package/dist/tools/perf.js +74 -7
  35. package/dist/tools/perf.js.map +1 -1
  36. package/dist/tools/scripts.js +162 -6
  37. package/dist/tools/scripts.js.map +1 -1
  38. package/dist/tools/spatial.js +135 -0
  39. package/dist/tools/spatial.js.map +1 -0
  40. package/dist/tools/universe.js +177 -0
  41. package/dist/tools/universe.js.map +1 -0
  42. package/dist/tools/upload.js +294 -0
  43. package/dist/tools/upload.js.map +1 -0
  44. package/dist/tools/world.js +675 -51
  45. package/dist/tools/world.js.map +1 -1
  46. package/package.json +2 -2
  47. package/plugin/src/Commands.luau +31 -7
  48. package/plugin/src/Config.luau +65 -65
  49. package/plugin/src/Console.luau +1909 -1843
  50. package/plugin/src/Emulation.luau +172 -0
  51. package/plugin/src/Phrase.luau +816 -618
  52. package/plugin/src/Png.luau +8 -4
  53. package/plugin/src/Prompt.luau +965 -961
  54. package/plugin/src/Secret.luau +86 -0
  55. package/plugin/src/Serialize.luau +440 -8
  56. package/plugin/src/Undo.luau +94 -6
  57. package/plugin/src/handlers/Anim.luau +897 -0
  58. package/plugin/src/handlers/Assets.luau +286 -2
  59. package/plugin/src/handlers/Audio.luau +411 -0
  60. package/plugin/src/handlers/Capture.luau +155 -20
  61. package/plugin/src/handlers/Character.luau +823 -361
  62. package/plugin/src/handlers/Data.luau +539 -0
  63. package/plugin/src/handlers/Device.luau +394 -139
  64. package/plugin/src/handlers/Discover.luau +685 -363
  65. package/plugin/src/handlers/Geometry.luau +722 -450
  66. package/plugin/src/handlers/Instances.luau +84 -4
  67. package/plugin/src/handlers/Perf.luau +227 -0
  68. package/plugin/src/handlers/Scripts.luau +673 -539
  69. package/plugin/src/handlers/Session.luau +3 -0
  70. package/plugin/src/handlers/Spatial.luau +334 -0
  71. package/plugin/src/handlers/Viewport.luau +268 -0
  72. package/plugin/src/handlers/World.luau +89 -15
  73. package/plugin/src/init.server.luau +9 -1
  74. package/scripts/build-plugin.mjs +20 -0
  75. package/scripts/check-plugin.mjs +171 -124
@@ -20,6 +20,75 @@ local ChangeHistoryService = game:GetService("ChangeHistoryService")
20
20
 
21
21
  local Undo = {}
22
22
 
23
+ --[[
24
+ One recording at a time, per session.
25
+
26
+ `onCommand` runs every command on its own task so a slow handler cannot
27
+ stall the stream, which is right -- and it means two agents pointed at the
28
+ same edit session have their batches in flight at once. Nothing below this
29
+ line is safe under that. `TryBeginRecording` returns nil while another
30
+ recording is open, so the second batch would run with no recording of its
31
+ own: its changes land inside the FIRST batch's undo step, one Ctrl+Z reverts
32
+ both, and if the first batch fails its `Cancel` reverts the second agent's
33
+ work as well -- work that was already reported as done.
34
+
35
+ Today's handlers do not yield inside a recording, so Luau's cooperative
36
+ scheduling hides all of that. That is an accident of how they happen to be
37
+ written, not a property of the design: the first `*Async` call added inside
38
+ a `record` body would open the window silently, and the symptom would be a
39
+ rolled-back edit in someone else's session. So the guarantee is made
40
+ structural here instead of being left to every handler to preserve.
41
+
42
+ Mutations queue; reads never come through this file at all, so they stay
43
+ parallel. Uncontended -- the normal case, one agent -- this costs one
44
+ boolean test.
45
+ ]]
46
+ local held = false
47
+ local owner: thread? = nil
48
+ local waiting: { thread } = {}
49
+
50
+ local function acquire()
51
+ --[[
52
+ A recording opened inside another recording is the same thread, and
53
+ waiting for a lock it already holds would hang the session outright.
54
+ Nesting is not used today; this makes adding it a coarser undo step
55
+ rather than a deadlock.
56
+ ]]
57
+ if held and owner == coroutine.running() then
58
+ return false
59
+ end
60
+
61
+ if held then
62
+ table.insert(waiting, coroutine.running())
63
+ -- Resumed by `release`, which hands the lock over rather than dropping
64
+ -- it, so there is nothing to re-check on the way out.
65
+ coroutine.yield()
66
+ return true
67
+ end
68
+
69
+ held = true
70
+ owner = coroutine.running()
71
+ return true
72
+ end
73
+
74
+ local function release(acquired: boolean)
75
+ if not acquired then
76
+ return
77
+ end
78
+
79
+ local nextThread = table.remove(waiting, 1)
80
+ if nextThread == nil then
81
+ held = false
82
+ owner = nil
83
+ return
84
+ end
85
+
86
+ -- `held` deliberately stays true: the lock passes straight to the waiter,
87
+ -- so a command arriving in between cannot jump the queue.
88
+ owner = nextThread
89
+ task.spawn(nextThread)
90
+ end
91
+
23
92
  --[[
24
93
  Drops the redo entry a cancelled recording leaves behind.
25
94
 
@@ -52,10 +121,12 @@ end
52
121
  Runs `body` inside a named recording. Returns its result, and whether a
53
122
  recording was actually opened.
54
123
 
55
- `TryBeginRecording` returns nil when another recording is already open (for
56
- example the user is mid-drag in the viewport) or when history is unavailable.
57
- The work still runs in that case -- refusing to act would be worse than a
58
- slightly coarser undo stack -- it simply joins the surrounding recording.
124
+ `TryBeginRecording` returns nil when a recording is already open or when
125
+ history is unavailable. Since the lock above, that can no longer be another
126
+ MCP batch: it is Studio's own, the user mid-drag in the viewport. The work
127
+ still runs in that case -- refusing to act would be worse than a slightly
128
+ coarser undo stack -- it simply joins the surrounding recording, and the lock
129
+ is still held throughout so the batch stays whole.
59
130
 
60
131
  That second return value is not decoration. Whether an operation is undoable
61
132
  is something tools tell the agent, and therefore the user, so it has to be
@@ -63,19 +134,28 @@ end
63
134
  to no undo entry at all is worse than no claim.
64
135
  ]]
65
136
  function Undo.record<T>(name: string, displayName: string, body: () -> T): (T, boolean)
137
+ local acquired = acquire()
138
+
66
139
  local identifier = ChangeHistoryService:TryBeginRecording(name, displayName)
67
140
  if not identifier then
68
- return body(), false
141
+ local ok, result = pcall(body)
142
+ release(acquired)
143
+ if not ok then
144
+ error(result, 0)
145
+ end
146
+ return result, false
69
147
  end
70
148
 
71
149
  local ok, result = pcall(body)
72
150
  if ok then
73
151
  ChangeHistoryService:FinishRecording(identifier, Enum.FinishRecordingOperation.Commit)
152
+ release(acquired)
74
153
  return result, true
75
154
  end
76
155
 
77
156
  ChangeHistoryService:FinishRecording(identifier, Enum.FinishRecordingOperation.Cancel)
78
157
  dropRedo()
158
+ release(acquired)
79
159
  error(result, 0)
80
160
  end
81
161
 
@@ -85,13 +165,21 @@ end
85
165
  and therefore tell the agent exactly what did and did not apply.
86
166
  ]]
87
167
  function Undo.recordPartial<T>(name: string, displayName: string, body: () -> T): (T, boolean)
168
+ local acquired = acquire()
169
+
88
170
  local identifier = ChangeHistoryService:TryBeginRecording(name, displayName)
89
171
  if not identifier then
90
- return body(), false
172
+ local ok, result = pcall(body)
173
+ release(acquired)
174
+ if not ok then
175
+ error(result, 0)
176
+ end
177
+ return result, false
91
178
  end
92
179
 
93
180
  local ok, result = pcall(body)
94
181
  ChangeHistoryService:FinishRecording(identifier, Enum.FinishRecordingOperation.Commit)
182
+ release(acquired)
95
183
  if not ok then
96
184
  error(result, 0)
97
185
  end