rbxstudio-mcp 2.3.1 → 2.4.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.
Files changed (175) hide show
  1. package/README.md +67 -14
  2. package/dist/__tests__/bridge-service.test.js +25 -13
  3. package/dist/__tests__/bridge-service.test.js.map +1 -1
  4. package/dist/__tests__/bridge-session.test.d.ts +2 -0
  5. package/dist/__tests__/bridge-session.test.d.ts.map +1 -0
  6. package/dist/__tests__/bridge-session.test.js +171 -0
  7. package/dist/__tests__/bridge-session.test.js.map +1 -0
  8. package/dist/__tests__/chunker.test.d.ts +2 -0
  9. package/dist/__tests__/chunker.test.d.ts.map +1 -0
  10. package/dist/__tests__/chunker.test.js +201 -0
  11. package/dist/__tests__/chunker.test.js.map +1 -0
  12. package/dist/__tests__/docs-core.test.d.ts +2 -0
  13. package/dist/__tests__/docs-core.test.d.ts.map +1 -0
  14. package/dist/__tests__/docs-core.test.js +137 -0
  15. package/dist/__tests__/docs-core.test.js.map +1 -0
  16. package/dist/__tests__/docs-fetcher.test.d.ts +2 -0
  17. package/dist/__tests__/docs-fetcher.test.d.ts.map +1 -0
  18. package/dist/__tests__/docs-fetcher.test.js +173 -0
  19. package/dist/__tests__/docs-fetcher.test.js.map +1 -0
  20. package/dist/__tests__/helpers.d.ts +8 -0
  21. package/dist/__tests__/helpers.d.ts.map +1 -0
  22. package/dist/__tests__/helpers.js +23 -0
  23. package/dist/__tests__/helpers.js.map +1 -0
  24. package/dist/__tests__/http-routes.test.d.ts +2 -0
  25. package/dist/__tests__/http-routes.test.d.ts.map +1 -0
  26. package/dist/__tests__/http-routes.test.js +233 -0
  27. package/dist/__tests__/http-routes.test.js.map +1 -0
  28. package/dist/__tests__/http-server.test.js +13 -6
  29. package/dist/__tests__/http-server.test.js.map +1 -1
  30. package/dist/__tests__/integration.test.js +9 -4
  31. package/dist/__tests__/integration.test.js.map +1 -1
  32. package/dist/__tests__/semantic-search.test.d.ts +2 -0
  33. package/dist/__tests__/semantic-search.test.d.ts.map +1 -0
  34. package/dist/__tests__/semantic-search.test.js +202 -0
  35. package/dist/__tests__/semantic-search.test.js.map +1 -0
  36. package/dist/__tests__/smoke.test.js +7 -3
  37. package/dist/__tests__/smoke.test.js.map +1 -1
  38. package/dist/__tests__/studio-client.test.d.ts +2 -0
  39. package/dist/__tests__/studio-client.test.d.ts.map +1 -0
  40. package/dist/__tests__/studio-client.test.js +25 -0
  41. package/dist/__tests__/studio-client.test.js.map +1 -0
  42. package/dist/__tests__/tool-nudges.test.d.ts +2 -0
  43. package/dist/__tests__/tool-nudges.test.d.ts.map +1 -0
  44. package/dist/__tests__/tool-nudges.test.js +60 -0
  45. package/dist/__tests__/tool-nudges.test.js.map +1 -0
  46. package/dist/__tests__/tool-registry.test.d.ts +2 -0
  47. package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
  48. package/dist/__tests__/tool-registry.test.js +365 -0
  49. package/dist/__tests__/tool-registry.test.js.map +1 -0
  50. package/dist/__tests__/tools-bridge.test.d.ts +2 -0
  51. package/dist/__tests__/tools-bridge.test.d.ts.map +1 -0
  52. package/dist/__tests__/tools-bridge.test.js +396 -0
  53. package/dist/__tests__/tools-bridge.test.js.map +1 -0
  54. package/dist/__tests__/tools-docs.test.d.ts +2 -0
  55. package/dist/__tests__/tools-docs.test.d.ts.map +1 -0
  56. package/dist/__tests__/tools-docs.test.js +112 -0
  57. package/dist/__tests__/tools-docs.test.js.map +1 -0
  58. package/dist/__tests__/tools-guards.test.d.ts +2 -0
  59. package/dist/__tests__/tools-guards.test.d.ts.map +1 -0
  60. package/dist/__tests__/tools-guards.test.js +131 -0
  61. package/dist/__tests__/tools-guards.test.js.map +1 -0
  62. package/dist/__tests__/tools-runtime.test.d.ts +2 -0
  63. package/dist/__tests__/tools-runtime.test.d.ts.map +1 -0
  64. package/dist/__tests__/tools-runtime.test.js +214 -0
  65. package/dist/__tests__/tools-runtime.test.js.map +1 -0
  66. package/dist/__tests__/tools-visual.test.d.ts +2 -0
  67. package/dist/__tests__/tools-visual.test.d.ts.map +1 -0
  68. package/dist/__tests__/tools-visual.test.js +149 -0
  69. package/dist/__tests__/tools-visual.test.js.map +1 -0
  70. package/dist/bridge-service.d.ts +99 -12
  71. package/dist/bridge-service.d.ts.map +1 -1
  72. package/dist/bridge-service.js +238 -21
  73. package/dist/bridge-service.js.map +1 -1
  74. package/dist/docs/cache.d.ts +50 -0
  75. package/dist/docs/cache.d.ts.map +1 -0
  76. package/dist/docs/cache.js +123 -0
  77. package/dist/docs/cache.js.map +1 -0
  78. package/dist/docs/embeddings/chunker.d.ts +120 -0
  79. package/dist/docs/embeddings/chunker.d.ts.map +1 -0
  80. package/dist/docs/embeddings/chunker.js +395 -0
  81. package/dist/docs/embeddings/chunker.js.map +1 -0
  82. package/dist/docs/embeddings/embedder.d.ts +41 -0
  83. package/dist/docs/embeddings/embedder.d.ts.map +1 -0
  84. package/dist/docs/embeddings/embedder.js +113 -0
  85. package/dist/docs/embeddings/embedder.js.map +1 -0
  86. package/dist/docs/embeddings/index.d.ts +102 -0
  87. package/dist/docs/embeddings/index.d.ts.map +1 -0
  88. package/dist/docs/embeddings/index.js +250 -0
  89. package/dist/docs/embeddings/index.js.map +1 -0
  90. package/dist/docs/embeddings/manager.d.ts +68 -0
  91. package/dist/docs/embeddings/manager.d.ts.map +1 -0
  92. package/dist/docs/embeddings/manager.js +97 -0
  93. package/dist/docs/embeddings/manager.js.map +1 -0
  94. package/dist/docs/fetcher.d.ts +29 -0
  95. package/dist/docs/fetcher.d.ts.map +1 -0
  96. package/dist/docs/fetcher.js +244 -0
  97. package/dist/docs/fetcher.js.map +1 -0
  98. package/dist/docs/reference.d.ts +37 -0
  99. package/dist/docs/reference.d.ts.map +1 -0
  100. package/dist/docs/reference.js +108 -0
  101. package/dist/docs/reference.js.map +1 -0
  102. package/dist/docs/search.d.ts +194 -0
  103. package/dist/docs/search.d.ts.map +1 -0
  104. package/dist/docs/search.js +733 -0
  105. package/dist/docs/search.js.map +1 -0
  106. package/dist/http-server.d.ts.map +1 -1
  107. package/dist/http-server.js +52 -5
  108. package/dist/http-server.js.map +1 -1
  109. package/dist/index.d.ts +8 -9
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +35 -1035
  112. package/dist/index.js.map +1 -1
  113. package/dist/instructions.d.ts +15 -0
  114. package/dist/instructions.d.ts.map +1 -0
  115. package/dist/instructions.js +26 -0
  116. package/dist/instructions.js.map +1 -0
  117. package/dist/tools/defs/attributes.d.ts +6 -0
  118. package/dist/tools/defs/attributes.d.ts.map +1 -0
  119. package/dist/tools/defs/attributes.js +85 -0
  120. package/dist/tools/defs/attributes.js.map +1 -0
  121. package/dist/tools/defs/docs.d.ts +17 -0
  122. package/dist/tools/defs/docs.d.ts.map +1 -0
  123. package/dist/tools/defs/docs.js +151 -0
  124. package/dist/tools/defs/docs.js.map +1 -0
  125. package/dist/tools/defs/execute.d.ts +6 -0
  126. package/dist/tools/defs/execute.d.ts.map +1 -0
  127. package/dist/tools/defs/execute.js +21 -0
  128. package/dist/tools/defs/execute.js.map +1 -0
  129. package/dist/tools/defs/inspection.d.ts +7 -0
  130. package/dist/tools/defs/inspection.d.ts.map +1 -0
  131. package/dist/tools/defs/inspection.js +202 -0
  132. package/dist/tools/defs/inspection.js.map +1 -0
  133. package/dist/tools/defs/objects.d.ts +6 -0
  134. package/dist/tools/defs/objects.d.ts.map +1 -0
  135. package/dist/tools/defs/objects.js +111 -0
  136. package/dist/tools/defs/objects.js.map +1 -0
  137. package/dist/tools/defs/properties.d.ts +6 -0
  138. package/dist/tools/defs/properties.d.ts.map +1 -0
  139. package/dist/tools/defs/properties.js +71 -0
  140. package/dist/tools/defs/properties.js.map +1 -0
  141. package/dist/tools/defs/runtime.d.ts +6 -0
  142. package/dist/tools/defs/runtime.d.ts.map +1 -0
  143. package/dist/tools/defs/runtime.js +145 -0
  144. package/dist/tools/defs/runtime.js.map +1 -0
  145. package/dist/tools/defs/scripts.d.ts +18 -0
  146. package/dist/tools/defs/scripts.d.ts.map +1 -0
  147. package/dist/tools/defs/scripts.js +163 -0
  148. package/dist/tools/defs/scripts.js.map +1 -0
  149. package/dist/tools/defs/tags.d.ts +6 -0
  150. package/dist/tools/defs/tags.d.ts.map +1 -0
  151. package/dist/tools/defs/tags.js +74 -0
  152. package/dist/tools/defs/tags.js.map +1 -0
  153. package/dist/tools/defs/visual.d.ts +7 -0
  154. package/dist/tools/defs/visual.d.ts.map +1 -0
  155. package/dist/tools/defs/visual.js +208 -0
  156. package/dist/tools/defs/visual.js.map +1 -0
  157. package/dist/tools/index.d.ts +101 -25
  158. package/dist/tools/index.d.ts.map +1 -1
  159. package/dist/tools/index.js +580 -63
  160. package/dist/tools/index.js.map +1 -1
  161. package/dist/tools/nudges.d.ts +25 -0
  162. package/dist/tools/nudges.d.ts.map +1 -0
  163. package/dist/tools/nudges.js +34 -0
  164. package/dist/tools/nudges.js.map +1 -0
  165. package/dist/tools/registry.d.ts +20 -0
  166. package/dist/tools/registry.d.ts.map +1 -0
  167. package/dist/tools/registry.js +65 -0
  168. package/dist/tools/registry.js.map +1 -0
  169. package/dist/tools/types.d.ts +24 -0
  170. package/dist/tools/types.d.ts.map +1 -0
  171. package/dist/tools/types.js +2 -0
  172. package/dist/tools/types.js.map +1 -0
  173. package/package.json +7 -6
  174. package/studio-plugin/MCPPlugin.rbxmx +3 -238
  175. package/studio-plugin/plugin.luau +2041 -365
@@ -0,0 +1,131 @@
1
+ import { RobloxStudioTools } from '../tools/index';
2
+ import { parseToolText } from './helpers';
3
+ function setup(response = { success: true }) {
4
+ const bridge = {
5
+ sendRequest: jest.fn().mockResolvedValue(response),
6
+ };
7
+ const tools = new RobloxStudioTools(bridge);
8
+ return { bridge, tools };
9
+ }
10
+ describe('RobloxStudioTools argument validation', () => {
11
+ test.each([
12
+ ['getInstanceProperties', (tools) => tools.getInstanceProperties(''), /Instance path is required/],
13
+ ['getInstanceChildren', (tools) => tools.getInstanceChildren(''), /Instance path is required/],
14
+ ['searchByProperty', (tools) => tools.searchByProperty('', 'value'), /Property name and value/],
15
+ ['getClassInfo', (tools) => tools.getClassInfo(''), /Class name is required/],
16
+ ['grep', (tools) => tools.grep(''), /pattern is required/],
17
+ ['setProperty', (tools) => tools.setProperty('', 'Name', 'x'), /Instance path and property name/],
18
+ ['massSetProperty', (tools) => tools.massSetProperty([], 'Name', 'x'), /Paths array/],
19
+ ['massGetProperty', (tools) => tools.massGetProperty([], 'Name'), /Paths array/],
20
+ ['createObject', (tools) => tools.createObject('', 'game'), /Class name and parent/],
21
+ ['createObjectWithProperties', (tools) => tools.createObjectWithProperties('', 'game'), /Class name and parent/],
22
+ ['massCreateObjects', (tools) => tools.massCreateObjects([]), /Objects array/],
23
+ [
24
+ 'massCreateObjectsWithProperties',
25
+ (tools) => tools.massCreateObjectsWithProperties([]),
26
+ /Objects array/,
27
+ ],
28
+ ['deleteObject', (tools) => tools.deleteObject(''), /Instance path is required/],
29
+ ['smartDuplicate', (tools) => tools.smartDuplicate('game.Workspace.Part', 0), /count > 0/],
30
+ ['massDuplicate', (tools) => tools.massDuplicate([]), /Duplications array/],
31
+ [
32
+ 'setCalculatedProperty',
33
+ (tools) => tools.setCalculatedProperty([], 'Name', 'formula'),
34
+ /Paths, property name, and formula/,
35
+ ],
36
+ [
37
+ 'setRelativeProperty',
38
+ (tools) => tools.setRelativeProperty([], 'Name', 'add', 1),
39
+ /Paths, property name, operation, and value/,
40
+ ],
41
+ ['getScriptSource', (tools) => tools.getScriptSource(''), /Instance path is required/],
42
+ [
43
+ 'setScriptSource',
44
+ (tools) => tools.setScriptSource('game.Script', undefined),
45
+ /source code string/,
46
+ ],
47
+ ['editScript missing path', (tools) => tools.editScript('', 'a', 'b'), /Instance path/],
48
+ ['editScript missing old', (tools) => tools.editScript('game.Script', undefined, 'b'), /old_string/],
49
+ ['editScript missing new', (tools) => tools.editScript('game.Script', 'a', undefined), /new_string/],
50
+ ['editScript identical', (tools) => tools.editScript('game.Script', 'a', 'a'), /must be different/],
51
+ ['searchScript', (tools) => tools.searchScript('', 'pattern'), /Instance path and pattern/],
52
+ ['getScriptFunction', (tools) => tools.getScriptFunction('game.Script', ''), /function name/],
53
+ ['findAndReplaceInScripts paths', (tools) => tools.findAndReplaceInScripts([], 'a', 'b'), /Paths array/],
54
+ [
55
+ 'findAndReplaceInScripts strings',
56
+ (tools) => tools.findAndReplaceInScripts(['game.Script'], undefined, 'b'),
57
+ /old_string and new_string/,
58
+ ],
59
+ ['getAttribute', (tools) => tools.getAttribute('', 'Health'), /Instance path and attribute name/],
60
+ ['setAttribute', (tools) => tools.setAttribute('', 'Health', 1), /Instance path and attribute name/],
61
+ ['getAttributes', (tools) => tools.getAttributes(''), /Instance path is required/],
62
+ ['deleteAttribute', (tools) => tools.deleteAttribute('', 'Health'), /Instance path and attribute name/],
63
+ ['getTags', (tools) => tools.getTags(''), /Instance path is required/],
64
+ ['addTag', (tools) => tools.addTag('', 'Enemy'), /Instance path and tag name/],
65
+ ['removeTag', (tools) => tools.removeTag('', 'Enemy'), /Instance path and tag name/],
66
+ ['getTagged', (tools) => tools.getTagged(''), /Tag name is required/],
67
+ ['cloneInstance', (tools) => tools.cloneInstance('', 'game'), /Source path and target parent/],
68
+ ['moveInstance', (tools) => tools.moveInstance('', 'game'), /Instance path and new parent/],
69
+ ['validateScript', (tools) => tools.validateScript(), /Either instance path or source/],
70
+ ['insertAsset', (tools) => tools.insertAsset(0), /Asset ID is required/],
71
+ ['renderObjectView', (tools) => tools.renderObjectView(''), /Instance path is required/],
72
+ ['focusCamera', (tools) => tools.focusCamera(''), /Instance path is required/],
73
+ ['executeLua', (tools) => tools.executeLua(''), /Code is required/],
74
+ ['searchRobloxDocs', (tools) => tools.searchRobloxDocs(''), /query is required/],
75
+ ['getRobloxDoc', (tools) => tools.getRobloxDoc(''), /path is required/],
76
+ ['getRobloxApiReference', (tools) => tools.getRobloxApiReference(''), /name is required/],
77
+ ])('%s rejects invalid input before bridge use', async (_name, call, message) => {
78
+ const { bridge, tools } = setup();
79
+ await expect(call(tools)).rejects.toThrow(message);
80
+ expect(bridge.sendRequest).not.toHaveBeenCalled();
81
+ });
82
+ });
83
+ describe('RobloxStudioTools MCP-internal companion guards', () => {
84
+ test.each([
85
+ ['deleteObject', (tools) => tools.deleteObject('game.ServerScriptService._MCPTestCompanion')],
86
+ ['setScriptSource', (tools) => tools.setScriptSource('game.ServerScriptService._MCPTestCompanion', 'print(1)')],
87
+ ['editScript', (tools) => tools.editScript('game.ServerScriptService._MCPTestCompanion', 'a', 'b')],
88
+ ['cloneInstance', (tools) => tools.cloneInstance('game.ServerScriptService._MCPTestCompanion', 'game.Workspace')],
89
+ ['moveInstance', (tools) => tools.moveInstance('game.ServerScriptService._MCPTestCompanion', 'game.Workspace')],
90
+ ])('%s refuses to mutate companion scripts', async (_name, call) => {
91
+ const { bridge, tools } = setup();
92
+ const body = parseToolText(await call(tools));
93
+ expect(body).toMatchObject({ success: false, error: 'mcp_internal' });
94
+ expect(body.message).toContain('_MCPTestCompanion');
95
+ expect(bridge.sendRequest).not.toHaveBeenCalled();
96
+ });
97
+ test('setProperty only blocks dangerous properties on companion scripts', async () => {
98
+ const { bridge, tools } = setup();
99
+ const blocked = parseToolText(await tools.setProperty('game.ServerScriptService._MCPTestCompanion', 'Disabled', true));
100
+ expect(blocked).toMatchObject({ success: false, error: 'mcp_internal' });
101
+ expect(bridge.sendRequest).not.toHaveBeenCalled();
102
+ await tools.setProperty('game.ServerScriptService._MCPTestCompanion', 'DisplayName', 'ok');
103
+ expect(bridge.sendRequest).toHaveBeenCalledWith('/api/set-property', {
104
+ instancePath: 'game.ServerScriptService._MCPTestCompanion',
105
+ propertyName: 'DisplayName',
106
+ propertyValue: 'ok',
107
+ });
108
+ });
109
+ test('findAndReplaceInScripts skips companion scripts but still edits allowed scripts', async () => {
110
+ const { bridge, tools } = setup({ success: true, changed: 1 });
111
+ const body = parseToolText(await tools.findAndReplaceInScripts(['game.ServerScriptService._MCPTestCompanion', 'game.ServerScriptService.Main'], 'old', 'new'));
112
+ expect(bridge.sendRequest).toHaveBeenCalledWith('/api/find-and-replace-in-scripts', {
113
+ paths: ['game.ServerScriptService.Main'],
114
+ oldString: 'old',
115
+ newString: 'new',
116
+ validateAfter: true,
117
+ });
118
+ expect(body).toMatchObject({
119
+ success: true,
120
+ changed: 1,
121
+ skippedMCPInternal: ['game.ServerScriptService._MCPTestCompanion'],
122
+ });
123
+ });
124
+ test('findAndReplaceInScripts refuses all-companion batches', async () => {
125
+ const { bridge, tools } = setup();
126
+ const body = parseToolText(await tools.findAndReplaceInScripts(['game.StarterPlayer._MCPTestCompanion_Client'], 'old', 'new'));
127
+ expect(body).toMatchObject({ success: false, error: 'mcp_internal' });
128
+ expect(bridge.sendRequest).not.toHaveBeenCalled();
129
+ });
130
+ });
131
+ //# sourceMappingURL=tools-guards.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-guards.test.js","sourceRoot":"","sources":["../../src/__tests__/tools-guards.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tools-runtime.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-runtime.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/tools-runtime.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,214 @@
1
+ import { BridgeService } from '../bridge-service';
2
+ import { RobloxStudioTools } from '../tools/index';
3
+ import { parseToolText } from './helpers';
4
+ function setupWithRealBridge(response = { success: true }) {
5
+ const bridge = new BridgeService();
6
+ const sendRequest = jest.spyOn(bridge, 'sendRequest').mockResolvedValue(response);
7
+ const tools = new RobloxStudioTools(bridge);
8
+ return { bridge, sendRequest, tools };
9
+ }
10
+ describe('play-test runtime tools', () => {
11
+ afterEach(() => {
12
+ jest.useRealTimers();
13
+ });
14
+ test('playSolo starts a bridge session and passes its sessionId to Studio', async () => {
15
+ const { bridge, sendRequest, tools } = setupWithRealBridge({ success: true, mode: 'play' });
16
+ const body = parseToolText(await tools.playSolo());
17
+ expect(body).toMatchObject({
18
+ success: true,
19
+ mode: 'play',
20
+ note: 'Use get_playtest_output to read logs; stop_play to end.',
21
+ });
22
+ expect(body.sessionId).toBe(bridge.testSession?.sessionId);
23
+ expect(sendRequest).toHaveBeenCalledWith('/api/play-solo', { sessionId: body.sessionId });
24
+ expect(bridge.testSession?.status).toBe('active');
25
+ });
26
+ test('playSolo marks the bridge session ended when Studio rejects the start request', async () => {
27
+ const bridge = new BridgeService();
28
+ jest.spyOn(bridge, 'sendRequest').mockRejectedValue(new Error('studio unavailable'));
29
+ const tools = new RobloxStudioTools(bridge);
30
+ await expect(tools.playSolo()).rejects.toThrow('studio unavailable');
31
+ expect(bridge.testSession).toMatchObject({
32
+ status: 'ended',
33
+ endReason: 'plugin_request_failed',
34
+ });
35
+ });
36
+ test('stopPlay is idempotent when no session exists or the session already ended', async () => {
37
+ const { bridge, tools } = setupWithRealBridge();
38
+ expect(parseToolText(await tools.stopPlay())).toMatchObject({
39
+ success: true,
40
+ alreadyEnded: true,
41
+ });
42
+ const sessionId = bridge.startTestSession();
43
+ bridge.endTestSession(sessionId, 'user_stop');
44
+ expect(parseToolText(await tools.stopPlay())).toMatchObject({
45
+ success: true,
46
+ alreadyEnded: true,
47
+ sessionId,
48
+ endReason: 'user_stop',
49
+ });
50
+ });
51
+ test('stopPlay enqueues an end command and reports confirmed completion', async () => {
52
+ jest.useFakeTimers();
53
+ const { bridge, tools } = setupWithRealBridge();
54
+ const sessionId = bridge.startTestSession();
55
+ const stopPromise = tools.stopPlay();
56
+ const poll = bridge.popTestCommand(sessionId, 'server');
57
+ expect(poll.command).toEqual({ cmd: 'end', args: 'MCP_Stop' });
58
+ bridge.endTestSession(sessionId, 'companion_end');
59
+ const body = parseToolText(await stopPromise);
60
+ expect(body).toMatchObject({
61
+ success: true,
62
+ confirmed: true,
63
+ sessionId,
64
+ status: 'ended',
65
+ endReason: 'companion_end',
66
+ });
67
+ });
68
+ test('getPlaytestOutput reads the bridge buffer instead of calling Studio', async () => {
69
+ const { bridge, sendRequest, tools } = setupWithRealBridge();
70
+ const sessionId = bridge.startTestSession();
71
+ bridge.appendTestOutput(sessionId, [{ message: 'hello', messageType: 'MessageOutput', timestamp: 1 }]);
72
+ const body = parseToolText(await tools.getPlaytestOutput(0, 10, ['MessageOutput']));
73
+ expect(body).toMatchObject({
74
+ sessionId,
75
+ status: 'active',
76
+ nextSinceSeq: 1,
77
+ entries: [
78
+ {
79
+ seq: 1,
80
+ message: 'hello',
81
+ messageType: 'MessageOutput',
82
+ source: 'server',
83
+ },
84
+ ],
85
+ });
86
+ expect(sendRequest).not.toHaveBeenCalled();
87
+ });
88
+ });
89
+ describe('runLiveLua preflight and reply flow', () => {
90
+ test('returns structured failures for missing, ended, or unready sessions', async () => {
91
+ const { bridge, tools } = setupWithRealBridge();
92
+ expect(parseToolText(await tools.runLiveLua('return 1'))).toMatchObject({
93
+ success: false,
94
+ error: 'no_playtest',
95
+ });
96
+ const endedSessionId = bridge.startTestSession();
97
+ bridge.endTestSession(endedSessionId, 'done');
98
+ expect(parseToolText(await tools.runLiveLua('return 1'))).toMatchObject({
99
+ success: false,
100
+ error: 'playtest_ended',
101
+ });
102
+ bridge.startTestSession();
103
+ expect(parseToolText(await tools.runLiveLua('return 1'))).toMatchObject({
104
+ success: false,
105
+ error: 'companion_not_ready',
106
+ });
107
+ });
108
+ test('refuses server execution when the companion reports loadstring disabled', async () => {
109
+ const { bridge, tools } = setupWithRealBridge();
110
+ const sessionId = bridge.startTestSession();
111
+ bridge.popTestCommand(sessionId, 'server', { hello: { loadstringReady: false } });
112
+ expect(parseToolText(await tools.runLiveLua('return 1'))).toMatchObject({
113
+ success: false,
114
+ error: 'loadstring_disabled',
115
+ target: 'server',
116
+ });
117
+ });
118
+ test('executes server-targeted code through an eval command and resolves the reply', async () => {
119
+ const { bridge, tools } = setupWithRealBridge();
120
+ const sessionId = bridge.startTestSession();
121
+ bridge.popTestCommand(sessionId, 'server', { hello: { loadstringReady: true } });
122
+ const resultPromise = tools.runLiveLua('return 2', 'server', undefined, 1500, false);
123
+ const poll = bridge.popTestCommand(sessionId, 'server');
124
+ expect(poll.command?.cmd).toBe('eval');
125
+ expect(poll.command?.args).toMatchObject({
126
+ code: 'return 2',
127
+ timeoutMs: 1500,
128
+ captureLogs: false,
129
+ });
130
+ bridge.resolveEvalReply(sessionId, poll.command.args.replyId, {
131
+ ok: true,
132
+ values: [2],
133
+ logs: [{ message: 'ran', messageType: 'MessageOutput', timestamp: 1 }],
134
+ durationMs: 12,
135
+ });
136
+ expect(parseToolText(await resultPromise)).toMatchObject({
137
+ success: true,
138
+ target: 'server',
139
+ timeoutMs: 1500,
140
+ durationMs: 12,
141
+ values: [2],
142
+ logs: [{ message: 'ran' }],
143
+ });
144
+ });
145
+ test('maps eval error replies into structured failure output', async () => {
146
+ const { bridge, tools } = setupWithRealBridge();
147
+ const sessionId = bridge.startTestSession();
148
+ bridge.popTestCommand(sessionId, 'server', { hello: { loadstringReady: true } });
149
+ const resultPromise = tools.runLiveLua('error("boom")', 'server');
150
+ const poll = bridge.popTestCommand(sessionId, 'server');
151
+ bridge.resolveEvalReply(sessionId, poll.command.args.replyId, {
152
+ ok: false,
153
+ errorType: 'runtime_error',
154
+ error: 'boom',
155
+ traceback: 'trace',
156
+ });
157
+ expect(parseToolText(await resultPromise)).toMatchObject({
158
+ success: false,
159
+ error: 'runtime_error',
160
+ message: 'boom',
161
+ traceback: 'trace',
162
+ });
163
+ });
164
+ test('returns structured client-targeting failures before enqueueing eval', async () => {
165
+ const { bridge, tools } = setupWithRealBridge();
166
+ const sessionId = bridge.startTestSession();
167
+ expect(parseToolText(await tools.runLiveLua('return 1', 'client'))).toMatchObject({
168
+ success: false,
169
+ error: 'no_clients_connected',
170
+ });
171
+ bridge.popTestCommand(sessionId, 'client', {
172
+ userId: 1,
173
+ playerName: 'Alice',
174
+ hello: { loadstringReady: true },
175
+ });
176
+ bridge.popTestCommand(sessionId, 'client', {
177
+ userId: 2,
178
+ playerName: 'Bob',
179
+ hello: { loadstringReady: true },
180
+ });
181
+ expect(parseToolText(await tools.runLiveLua('return 1', 'client'))).toMatchObject({
182
+ success: false,
183
+ error: 'multiple_clients',
184
+ });
185
+ expect(parseToolText(await tools.runLiveLua('return 1', 'client', 'Carla'))).toMatchObject({
186
+ success: false,
187
+ error: 'no_such_player',
188
+ });
189
+ });
190
+ test('forwards client-targeted evals through the server companion queue', async () => {
191
+ const { bridge, tools } = setupWithRealBridge();
192
+ const sessionId = bridge.startTestSession();
193
+ bridge.popTestCommand(sessionId, 'client', {
194
+ userId: 1,
195
+ playerName: 'Alice',
196
+ hello: { loadstringReady: true },
197
+ });
198
+ const resultPromise = tools.runLiveLua('return game.Players.LocalPlayer.Name', 'client');
199
+ const poll = bridge.popTestCommand(sessionId, 'server');
200
+ expect(poll.command?.cmd).toBe('eval');
201
+ expect(poll.command?.args.forwardTo).toEqual({ userId: 1, playerName: 'Alice' });
202
+ bridge.resolveEvalReply(sessionId, poll.command.args.replyId, {
203
+ ok: true,
204
+ values: ['Alice'],
205
+ });
206
+ expect(parseToolText(await resultPromise)).toMatchObject({
207
+ success: true,
208
+ target: 'client',
209
+ player: { name: 'Alice', userId: 1 },
210
+ values: ['Alice'],
211
+ });
212
+ });
213
+ });
214
+ //# sourceMappingURL=tools-runtime.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-runtime.test.js","sourceRoot":"","sources":["../../src/__tests__/tools-runtime.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tools-visual.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-visual.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/tools-visual.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,149 @@
1
+ import { RobloxStudioTools } from '../tools/index';
2
+ import { parseToolText } from './helpers';
3
+ function setup(response) {
4
+ const bridge = {
5
+ sendRequest: jest.fn().mockResolvedValue(response),
6
+ };
7
+ return new RobloxStudioTools(bridge);
8
+ }
9
+ function onePixelRgbaBase64() {
10
+ return Buffer.from([255, 0, 0, 255]).toString('base64');
11
+ }
12
+ describe('visual tool PNG conversion', () => {
13
+ test('captureScreenshot converts RGBA payloads to PNG image content', async () => {
14
+ const tools = setup({
15
+ success: true,
16
+ message: 'captured',
17
+ originalWidth: 1,
18
+ originalHeight: 1,
19
+ width: 1,
20
+ height: 1,
21
+ base64: onePixelRgbaBase64(),
22
+ });
23
+ const result = await tools.captureScreenshot(1, 1);
24
+ const body = parseToolText(result);
25
+ expect(body).toMatchObject({
26
+ success: true,
27
+ message: 'captured',
28
+ width: 1,
29
+ height: 1,
30
+ format: 'PNG',
31
+ });
32
+ expect(result.content[1]).toMatchObject({
33
+ type: 'image',
34
+ mimeType: 'image/png',
35
+ });
36
+ expect(result.content[1].data).toMatch(/^iVBORw0KGgo/);
37
+ });
38
+ test('renderObjectView converts RGBA payloads to PNG image content', async () => {
39
+ const tools = setup({
40
+ success: true,
41
+ message: 'rendered',
42
+ viewInfo: { angle: 'iso' },
43
+ width: 1,
44
+ height: 1,
45
+ base64: onePixelRgbaBase64(),
46
+ });
47
+ const result = await tools.renderObjectView('game.Workspace.Part');
48
+ const body = parseToolText(result);
49
+ expect(body).toMatchObject({
50
+ success: true,
51
+ message: 'rendered',
52
+ viewInfo: { angle: 'iso' },
53
+ format: 'PNG',
54
+ });
55
+ expect(result.content[1]).toMatchObject({
56
+ type: 'image',
57
+ mimeType: 'image/png',
58
+ });
59
+ expect(result.content[1].data).toMatch(/^iVBORw0KGgo/);
60
+ });
61
+ test('renderGui converts RGBA payloads to PNG image content', async () => {
62
+ const tools = setup({
63
+ success: true,
64
+ message: 'rendered gui',
65
+ guiInfo: { objectClass: 'ScreenGui', renderedElements: 2 },
66
+ width: 1,
67
+ height: 1,
68
+ base64: onePixelRgbaBase64(),
69
+ });
70
+ const result = await tools.renderGui('game.StarterGui.MainMenu');
71
+ const body = parseToolText(result);
72
+ expect(body).toMatchObject({
73
+ success: true,
74
+ message: 'rendered gui',
75
+ guiInfo: { objectClass: 'ScreenGui', renderedElements: 2 },
76
+ format: 'PNG',
77
+ });
78
+ expect(result.content[1]).toMatchObject({
79
+ type: 'image',
80
+ mimeType: 'image/png',
81
+ });
82
+ expect(result.content[1].data).toMatch(/^iVBORw0KGgo/);
83
+ });
84
+ test('renderGui returns a structured conversion error for bad buffers', async () => {
85
+ const tools = setup({
86
+ success: true,
87
+ width: 2,
88
+ height: 2,
89
+ base64: onePixelRgbaBase64(),
90
+ });
91
+ const body = parseToolText(await tools.renderGui('game.StarterGui.MainMenu'));
92
+ expect(body).toMatchObject({ success: false });
93
+ expect(body.error).toContain('PNG conversion failed');
94
+ expect(body.error).toContain('Buffer size mismatch');
95
+ });
96
+ test('renderGui forwards instancePath and size caps, passing through non-image responses', async () => {
97
+ const bridge = {
98
+ sendRequest: jest.fn().mockResolvedValue({ success: false, error: 'not available' }),
99
+ };
100
+ const tools = new RobloxStudioTools(bridge);
101
+ const body = parseToolText(await tools.renderGui('game.StarterGui.HUD', { maxWidth: 512, maxHeight: 256 }));
102
+ expect(body).toEqual({ success: false, error: 'not available' });
103
+ expect(bridge.sendRequest).toHaveBeenCalledWith('/api/render-gui', {
104
+ instancePath: 'game.StarterGui.HUD',
105
+ region: 'element',
106
+ maxWidth: 512,
107
+ maxHeight: 256,
108
+ });
109
+ });
110
+ test('renderGui forwards an explicit screen region', async () => {
111
+ const bridge = {
112
+ sendRequest: jest.fn().mockResolvedValue({ success: false, error: 'not available' }),
113
+ };
114
+ const tools = new RobloxStudioTools(bridge);
115
+ await tools.renderGui('game.StarterGui.MainMenu', { region: 'screen' });
116
+ expect(bridge.sendRequest).toHaveBeenCalledWith('/api/render-gui', {
117
+ instancePath: 'game.StarterGui.MainMenu',
118
+ region: 'screen',
119
+ maxWidth: undefined,
120
+ maxHeight: undefined,
121
+ });
122
+ });
123
+ test('captureScreenshot returns a structured conversion error for bad buffers', async () => {
124
+ const tools = setup({
125
+ success: true,
126
+ width: 2,
127
+ height: 2,
128
+ base64: onePixelRgbaBase64(),
129
+ });
130
+ const body = parseToolText(await tools.captureScreenshot(2, 2));
131
+ expect(body.success).toBe(false);
132
+ expect(body.error).toContain('PNG conversion failed');
133
+ expect(body.error).toContain('Buffer size mismatch');
134
+ expect(body.originalResponse.width).toBe(2);
135
+ });
136
+ test('renderObjectView returns a structured conversion error for bad buffers', async () => {
137
+ const tools = setup({
138
+ success: true,
139
+ width: 2,
140
+ height: 2,
141
+ base64: onePixelRgbaBase64(),
142
+ });
143
+ const body = parseToolText(await tools.renderObjectView('game.Workspace.Part'));
144
+ expect(body).toMatchObject({ success: false });
145
+ expect(body.error).toContain('PNG conversion failed');
146
+ expect(body.error).toContain('Buffer size mismatch');
147
+ });
148
+ });
149
+ //# sourceMappingURL=tools-visual.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-visual.test.js","sourceRoot":"","sources":["../../src/__tests__/tools-visual.test.ts"],"names":[],"mappings":"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"}