@chrrxs/robloxstudio-mcp 2.11.4 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2066 -435
- package/package.json +1 -1
- package/studio-plugin/MCPPlugin.rbxmx +935 -298
- package/studio-plugin/src/modules/ClientBroker.ts +90 -36
- package/studio-plugin/src/modules/Communication.ts +118 -5
- package/studio-plugin/src/modules/EvalBridges.ts +60 -11
- package/studio-plugin/src/modules/LuauExec.ts +305 -0
- package/studio-plugin/src/modules/RenderMonitor.ts +60 -0
- package/studio-plugin/src/modules/StopPlayMonitor.ts +67 -31
- package/studio-plugin/src/modules/handlers/CaptureHandlers.ts +45 -3
- package/studio-plugin/src/modules/handlers/InputHandlers.ts +100 -39
- package/studio-plugin/src/modules/handlers/MetadataHandlers.ts +7 -146
- package/studio-plugin/src/modules/handlers/TestHandlers.ts +31 -8
- package/studio-plugin/src/server/index.server.ts +6 -0
- package/studio-plugin/src/types/index.d.ts +5 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,299 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// ../core/dist/bridge-service.js
|
|
13
|
+
import { v4 as uuidv4 } from "uuid";
|
|
14
|
+
function toPublic(inst) {
|
|
15
|
+
return {
|
|
16
|
+
instanceId: inst.instanceId,
|
|
17
|
+
role: inst.role,
|
|
18
|
+
placeId: inst.placeId,
|
|
19
|
+
placeName: inst.placeName,
|
|
20
|
+
dataModelName: inst.dataModelName,
|
|
21
|
+
isRunning: inst.isRunning,
|
|
22
|
+
lastActivity: inst.lastActivity,
|
|
23
|
+
connectedAt: inst.connectedAt
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
var RoutingFailure, STALE_INSTANCE_MS, BridgeService;
|
|
27
|
+
var init_bridge_service = __esm({
|
|
28
|
+
"../core/dist/bridge-service.js"() {
|
|
29
|
+
"use strict";
|
|
30
|
+
RoutingFailure = class extends Error {
|
|
31
|
+
routingError;
|
|
32
|
+
constructor(routingError) {
|
|
33
|
+
super(routingError.message);
|
|
34
|
+
this.name = "RoutingFailure";
|
|
35
|
+
this.routingError = routingError;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
STALE_INSTANCE_MS = 3e4;
|
|
39
|
+
BridgeService = class {
|
|
40
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
41
|
+
// Keyed by pluginSessionId (the per-plugin GUID).
|
|
42
|
+
instances = /* @__PURE__ */ new Map();
|
|
43
|
+
requestTimeout = 3e4;
|
|
44
|
+
registerInstance(input) {
|
|
45
|
+
const { pluginSessionId, instanceId, role } = input;
|
|
46
|
+
let assignedRole = role;
|
|
47
|
+
if (role === "client") {
|
|
48
|
+
const used = /* @__PURE__ */ new Set();
|
|
49
|
+
for (const inst of this.instances.values()) {
|
|
50
|
+
const match = inst.role.match(/^client-(\d+)$/);
|
|
51
|
+
if (match)
|
|
52
|
+
used.add(Number(match[1]));
|
|
53
|
+
}
|
|
54
|
+
let idx = 1;
|
|
55
|
+
while (used.has(idx))
|
|
56
|
+
idx++;
|
|
57
|
+
assignedRole = `client-${idx}`;
|
|
58
|
+
}
|
|
59
|
+
const existing = Array.from(this.instances.values()).find((i) => i.instanceId === instanceId && i.role === assignedRole && i.pluginSessionId !== pluginSessionId);
|
|
60
|
+
if (existing) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
error: {
|
|
64
|
+
code: "duplicate_instance_role",
|
|
65
|
+
message: `Another plugin is already registered as (${instanceId}, ${assignedRole}).`,
|
|
66
|
+
existing: toPublic(existing)
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
this.instances.set(pluginSessionId, {
|
|
71
|
+
pluginSessionId,
|
|
72
|
+
instanceId,
|
|
73
|
+
role: assignedRole,
|
|
74
|
+
placeId: input.placeId ?? 0,
|
|
75
|
+
placeName: input.placeName ?? "",
|
|
76
|
+
dataModelName: input.dataModelName ?? "",
|
|
77
|
+
isRunning: input.isRunning ?? false,
|
|
78
|
+
lastActivity: Date.now(),
|
|
79
|
+
connectedAt: Date.now()
|
|
80
|
+
});
|
|
81
|
+
return { ok: true, assignedRole, instanceId };
|
|
82
|
+
}
|
|
83
|
+
unregisterInstance(pluginSessionId) {
|
|
84
|
+
const removed = this.instances.get(pluginSessionId);
|
|
85
|
+
this.instances.delete(pluginSessionId);
|
|
86
|
+
if (!removed)
|
|
87
|
+
return;
|
|
88
|
+
for (const [id, req] of this.pendingRequests.entries()) {
|
|
89
|
+
const stillHasHandler = Array.from(this.instances.values()).some((i) => i.instanceId === req.targetInstanceId && i.role === req.targetRole);
|
|
90
|
+
if (!stillHasHandler) {
|
|
91
|
+
clearTimeout(req.timeoutId);
|
|
92
|
+
this.pendingRequests.delete(id);
|
|
93
|
+
req.reject(new Error(`Target (${req.targetInstanceId}, ${req.targetRole}) disconnected`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
getInstances() {
|
|
98
|
+
return Array.from(this.instances.values());
|
|
99
|
+
}
|
|
100
|
+
getPublicInstances() {
|
|
101
|
+
return this.getInstances().map(toPublic);
|
|
102
|
+
}
|
|
103
|
+
getInstanceBySessionId(pluginSessionId) {
|
|
104
|
+
return this.instances.get(pluginSessionId);
|
|
105
|
+
}
|
|
106
|
+
getPendingRequestCount() {
|
|
107
|
+
return this.pendingRequests.size;
|
|
108
|
+
}
|
|
109
|
+
updateInstanceActivity(pluginSessionId) {
|
|
110
|
+
const inst = this.instances.get(pluginSessionId);
|
|
111
|
+
if (inst) {
|
|
112
|
+
inst.lastActivity = Date.now();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
updateInstanceMetadata(pluginSessionId, metadata) {
|
|
116
|
+
const inst = this.instances.get(pluginSessionId);
|
|
117
|
+
if (!inst)
|
|
118
|
+
return;
|
|
119
|
+
if (metadata.placeId !== void 0)
|
|
120
|
+
inst.placeId = metadata.placeId;
|
|
121
|
+
if (metadata.placeName !== void 0)
|
|
122
|
+
inst.placeName = metadata.placeName;
|
|
123
|
+
if (metadata.dataModelName !== void 0)
|
|
124
|
+
inst.dataModelName = metadata.dataModelName;
|
|
125
|
+
if (metadata.isRunning !== void 0)
|
|
126
|
+
inst.isRunning = metadata.isRunning;
|
|
127
|
+
}
|
|
128
|
+
cleanupStaleInstances() {
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
for (const [id, inst] of this.instances.entries()) {
|
|
131
|
+
if (now - inst.lastActivity > STALE_INSTANCE_MS) {
|
|
132
|
+
this.unregisterInstance(id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Resolves (instance_id, target-role) MCP arguments to a concrete
|
|
137
|
+
// routing decision: either a single (instanceId, role) tuple or a fanout
|
|
138
|
+
// list. Returns an error result with the full instance list embedded so
|
|
139
|
+
// the caller (tool layer) can surface it without a second round-trip.
|
|
140
|
+
resolveTarget(input) {
|
|
141
|
+
const instances = this.getInstances();
|
|
142
|
+
const publicList = instances.map(toPublic);
|
|
143
|
+
const errorData = { instances: publicList, count: publicList.length };
|
|
144
|
+
const { instance_id, target } = input;
|
|
145
|
+
const isFanout = target === "all";
|
|
146
|
+
const role = target && target !== "all" ? target : void 0;
|
|
147
|
+
if (instance_id !== void 0) {
|
|
148
|
+
const matchingInstances = instances.filter((i) => i.instanceId === instance_id);
|
|
149
|
+
if (matchingInstances.length === 0) {
|
|
150
|
+
return {
|
|
151
|
+
ok: false,
|
|
152
|
+
error: {
|
|
153
|
+
code: "unrecognized_instance_id",
|
|
154
|
+
message: `instance_id "${instance_id}" is not connected. Pass one from data.instances.`,
|
|
155
|
+
data: errorData
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (isFanout) {
|
|
160
|
+
return {
|
|
161
|
+
ok: true,
|
|
162
|
+
mode: "fanout",
|
|
163
|
+
targets: matchingInstances.map((i) => ({
|
|
164
|
+
targetInstanceId: i.instanceId,
|
|
165
|
+
targetRole: i.role
|
|
166
|
+
}))
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (role) {
|
|
170
|
+
const exact = matchingInstances.find((i) => i.role === role);
|
|
171
|
+
if (!exact) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
error: {
|
|
175
|
+
code: "target_role_not_present_on_instance",
|
|
176
|
+
message: `instance "${instance_id}" has no role "${role}". Available roles: ${matchingInstances.map((i) => i.role).join(", ")}.`,
|
|
177
|
+
data: errorData
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return { ok: true, mode: "single", targetInstanceId: instance_id, targetRole: role };
|
|
182
|
+
}
|
|
183
|
+
if (matchingInstances.length === 1) {
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
mode: "single",
|
|
187
|
+
targetInstanceId: instance_id,
|
|
188
|
+
targetRole: matchingInstances[0].role
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const edit = matchingInstances.find((i) => i.role === "edit");
|
|
192
|
+
if (edit) {
|
|
193
|
+
return { ok: true, mode: "single", targetInstanceId: instance_id, targetRole: "edit" };
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
ok: false,
|
|
197
|
+
error: {
|
|
198
|
+
code: "target_role_required",
|
|
199
|
+
message: `instance "${instance_id}" has multiple roles connected: ${matchingInstances.map((i) => i.role).join(", ")}. Pass target=<role>.`,
|
|
200
|
+
data: errorData
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const distinctInstanceIds = new Set(instances.map((i) => i.instanceId));
|
|
205
|
+
if (distinctInstanceIds.size === 0) {
|
|
206
|
+
return {
|
|
207
|
+
ok: false,
|
|
208
|
+
error: {
|
|
209
|
+
code: "unrecognized_instance_id",
|
|
210
|
+
message: "No Studio plugin is connected.",
|
|
211
|
+
data: errorData
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (distinctInstanceIds.size > 1) {
|
|
216
|
+
const errorCode = role ? "ambiguous_target" : "multiple_instances_connected";
|
|
217
|
+
const msg = role ? `target=${role} is ambiguous: multiple places have this role. Pass instance_id.` : "Multiple Studio places are connected. Pass instance_id to disambiguate.";
|
|
218
|
+
return { ok: false, error: { code: errorCode, message: msg, data: errorData } };
|
|
219
|
+
}
|
|
220
|
+
const onlyInstanceId = instances[0].instanceId;
|
|
221
|
+
return this.resolveTarget({ instance_id: onlyInstanceId, target });
|
|
222
|
+
}
|
|
223
|
+
async sendRequest(endpoint, data, targetInstanceId, targetRole) {
|
|
224
|
+
const requestId = uuidv4();
|
|
225
|
+
return new Promise((resolve2, reject) => {
|
|
226
|
+
const timeoutId = setTimeout(() => {
|
|
227
|
+
if (this.pendingRequests.has(requestId)) {
|
|
228
|
+
this.pendingRequests.delete(requestId);
|
|
229
|
+
reject(new Error("Request timeout"));
|
|
230
|
+
}
|
|
231
|
+
}, this.requestTimeout);
|
|
232
|
+
const request = {
|
|
233
|
+
id: requestId,
|
|
234
|
+
endpoint,
|
|
235
|
+
data,
|
|
236
|
+
targetInstanceId,
|
|
237
|
+
targetRole,
|
|
238
|
+
timestamp: Date.now(),
|
|
239
|
+
resolve: resolve2,
|
|
240
|
+
reject,
|
|
241
|
+
timeoutId
|
|
242
|
+
};
|
|
243
|
+
this.pendingRequests.set(requestId, request);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
getPendingRequest(callerInstanceId, callerRole) {
|
|
247
|
+
let oldestRequest = null;
|
|
248
|
+
for (const request of this.pendingRequests.values()) {
|
|
249
|
+
if (request.targetInstanceId !== callerInstanceId)
|
|
250
|
+
continue;
|
|
251
|
+
if (request.targetRole !== callerRole)
|
|
252
|
+
continue;
|
|
253
|
+
if (!oldestRequest || request.timestamp < oldestRequest.timestamp) {
|
|
254
|
+
oldestRequest = request;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (oldestRequest) {
|
|
258
|
+
return {
|
|
259
|
+
requestId: oldestRequest.id,
|
|
260
|
+
request: {
|
|
261
|
+
endpoint: oldestRequest.endpoint,
|
|
262
|
+
data: oldestRequest.data
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
resolveRequest(requestId, response) {
|
|
269
|
+
const request = this.pendingRequests.get(requestId);
|
|
270
|
+
if (request) {
|
|
271
|
+
clearTimeout(request.timeoutId);
|
|
272
|
+
this.pendingRequests.delete(requestId);
|
|
273
|
+
request.resolve(response);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
rejectRequest(requestId, error) {
|
|
277
|
+
const request = this.pendingRequests.get(requestId);
|
|
278
|
+
if (request) {
|
|
279
|
+
clearTimeout(request.timeoutId);
|
|
280
|
+
this.pendingRequests.delete(requestId);
|
|
281
|
+
request.reject(error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
cleanupOldRequests() {
|
|
285
|
+
const now = Date.now();
|
|
286
|
+
for (const [id, request] of this.pendingRequests.entries()) {
|
|
287
|
+
if (now - request.timestamp > this.requestTimeout) {
|
|
288
|
+
clearTimeout(request.timeoutId);
|
|
289
|
+
this.pendingRequests.delete(id);
|
|
290
|
+
request.reject(new Error("Request timeout"));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
clearAllPendingRequests() {
|
|
295
|
+
for (const [, request] of this.pendingRequests.entries()) {
|
|
296
|
+
clearTimeout(request.timeoutId);
|
|
297
|
+
request.reject(new Error("Connection closed"));
|
|
298
|
+
}
|
|
299
|
+
this.pendingRequests.clear();
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
12
305
|
// ../core/dist/http-server.js
|
|
13
306
|
import express from "express";
|
|
14
307
|
import cors from "cors";
|
|
@@ -56,12 +349,7 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
56
349
|
version: serverConfig?.version,
|
|
57
350
|
pluginConnected: instances.length > 0,
|
|
58
351
|
instanceCount: instances.length,
|
|
59
|
-
instances: instances.map(
|
|
60
|
-
instanceId: i.instanceId,
|
|
61
|
-
role: i.role,
|
|
62
|
-
lastActivity: i.lastActivity,
|
|
63
|
-
connectedAt: i.connectedAt
|
|
64
|
-
})),
|
|
352
|
+
instances: instances.map(toPublic),
|
|
65
353
|
mcpServerActive: isMCPServerActive(),
|
|
66
354
|
uptime: mcpServerActive ? Date.now() - mcpServerStartTime : 0,
|
|
67
355
|
pendingRequests: bridge.getPendingRequestCount(),
|
|
@@ -70,22 +358,42 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
70
358
|
});
|
|
71
359
|
});
|
|
72
360
|
app.post("/ready", (req, res) => {
|
|
73
|
-
const { instanceId, role } = req.body;
|
|
74
|
-
if (instanceId
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
361
|
+
const { pluginSessionId, instanceId, role, placeId, placeName, dataModelName, isRunning } = req.body;
|
|
362
|
+
if (!pluginSessionId || !instanceId || !role) {
|
|
363
|
+
res.status(400).json({
|
|
364
|
+
success: false,
|
|
365
|
+
error: "pluginSessionId, instanceId, and role are required"
|
|
366
|
+
});
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const result = bridge.registerInstance({
|
|
370
|
+
pluginSessionId,
|
|
371
|
+
instanceId,
|
|
372
|
+
role,
|
|
373
|
+
placeId: typeof placeId === "number" ? placeId : 0,
|
|
374
|
+
placeName: typeof placeName === "string" ? placeName : "",
|
|
375
|
+
dataModelName: typeof dataModelName === "string" ? dataModelName : "",
|
|
376
|
+
isRunning: !!isRunning
|
|
377
|
+
});
|
|
378
|
+
if (!result.ok) {
|
|
379
|
+
res.status(409).json({
|
|
380
|
+
success: false,
|
|
381
|
+
error: result.error.code,
|
|
382
|
+
message: result.error.message,
|
|
383
|
+
existing: result.error.existing
|
|
384
|
+
});
|
|
385
|
+
return;
|
|
80
386
|
}
|
|
387
|
+
res.json({
|
|
388
|
+
success: true,
|
|
389
|
+
assignedRole: result.assignedRole,
|
|
390
|
+
instanceId: result.instanceId
|
|
391
|
+
});
|
|
81
392
|
});
|
|
82
393
|
app.post("/disconnect", (req, res) => {
|
|
83
|
-
const {
|
|
84
|
-
if (
|
|
85
|
-
bridge.unregisterInstance(
|
|
86
|
-
} else {
|
|
87
|
-
bridge.unregisterInstance("legacy");
|
|
88
|
-
bridge.clearAllPendingRequests();
|
|
394
|
+
const { pluginSessionId } = req.body;
|
|
395
|
+
if (pluginSessionId) {
|
|
396
|
+
bridge.unregisterInstance(pluginSessionId);
|
|
89
397
|
}
|
|
90
398
|
res.json({ success: true });
|
|
91
399
|
});
|
|
@@ -94,7 +402,7 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
94
402
|
res.json({
|
|
95
403
|
pluginConnected: instances.length > 0,
|
|
96
404
|
instanceCount: instances.length,
|
|
97
|
-
instances: instances.map(
|
|
405
|
+
instances: instances.map(toPublic),
|
|
98
406
|
mcpServerActive: isMCPServerActive(),
|
|
99
407
|
lastMCPActivity,
|
|
100
408
|
uptime: mcpServerActive ? Date.now() - mcpServerStartTime : 0
|
|
@@ -104,15 +412,17 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
104
412
|
res.json({ instances: bridge.getInstances() });
|
|
105
413
|
});
|
|
106
414
|
app.get("/poll", (req, res) => {
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
109
|
-
bridge.updateInstanceActivity(
|
|
415
|
+
const pluginSessionId = req.query.pluginSessionId;
|
|
416
|
+
if (pluginSessionId) {
|
|
417
|
+
bridge.updateInstanceActivity(pluginSessionId);
|
|
110
418
|
}
|
|
111
|
-
let
|
|
419
|
+
let callerInstanceId;
|
|
420
|
+
let callerRole;
|
|
112
421
|
let knownInstance = false;
|
|
113
|
-
if (
|
|
114
|
-
const inst = bridge.
|
|
422
|
+
if (pluginSessionId) {
|
|
423
|
+
const inst = bridge.getInstanceBySessionId(pluginSessionId);
|
|
115
424
|
if (inst) {
|
|
425
|
+
callerInstanceId = inst.instanceId;
|
|
116
426
|
callerRole = inst.role;
|
|
117
427
|
knownInstance = true;
|
|
118
428
|
}
|
|
@@ -127,7 +437,7 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
127
437
|
});
|
|
128
438
|
return;
|
|
129
439
|
}
|
|
130
|
-
const pendingRequest = bridge.getPendingRequest(callerRole);
|
|
440
|
+
const pendingRequest = knownInstance && callerInstanceId && callerRole ? bridge.getPendingRequest(callerInstanceId, callerRole) : null;
|
|
131
441
|
if (pendingRequest) {
|
|
132
442
|
res.json({
|
|
133
443
|
request: pendingRequest.request,
|
|
@@ -157,16 +467,16 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
157
467
|
res.json({ success: true });
|
|
158
468
|
});
|
|
159
469
|
app.post("/proxy", async (req, res) => {
|
|
160
|
-
const { endpoint, data,
|
|
161
|
-
if (!endpoint) {
|
|
162
|
-
res.status(400).json({ error: "endpoint
|
|
470
|
+
const { endpoint, data, targetInstanceId, targetRole, proxyInstanceId } = req.body;
|
|
471
|
+
if (!endpoint || !targetInstanceId || !targetRole) {
|
|
472
|
+
res.status(400).json({ error: "endpoint, targetInstanceId, and targetRole are required" });
|
|
163
473
|
return;
|
|
164
474
|
}
|
|
165
475
|
if (proxyInstanceId) {
|
|
166
476
|
proxyInstances.add(proxyInstanceId);
|
|
167
477
|
}
|
|
168
478
|
try {
|
|
169
|
-
const response = await bridge.sendRequest(endpoint, data,
|
|
479
|
+
const response = await bridge.sendRequest(endpoint, data, targetInstanceId, targetRole);
|
|
170
480
|
res.json({ response });
|
|
171
481
|
} catch (err) {
|
|
172
482
|
res.status(500).json({ error: err.message || "Proxy request failed" });
|
|
@@ -197,6 +507,19 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
197
507
|
try {
|
|
198
508
|
return await handler(tools, args || {});
|
|
199
509
|
} catch (error) {
|
|
510
|
+
if (error instanceof RoutingFailure) {
|
|
511
|
+
return {
|
|
512
|
+
content: [{
|
|
513
|
+
type: "text",
|
|
514
|
+
text: JSON.stringify({
|
|
515
|
+
error: error.routingError.code,
|
|
516
|
+
message: error.routingError.message,
|
|
517
|
+
data: error.routingError.data
|
|
518
|
+
})
|
|
519
|
+
}],
|
|
520
|
+
isError: true
|
|
521
|
+
};
|
|
522
|
+
}
|
|
200
523
|
if (error instanceof McpError)
|
|
201
524
|
throw error;
|
|
202
525
|
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -248,6 +571,14 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
|
|
|
248
571
|
const result = await handler(tools, req.body);
|
|
249
572
|
res.json(result);
|
|
250
573
|
} catch (error) {
|
|
574
|
+
if (error instanceof RoutingFailure) {
|
|
575
|
+
res.status(400).json({
|
|
576
|
+
error: error.routingError.code,
|
|
577
|
+
message: error.routingError.message,
|
|
578
|
+
data: error.routingError.data
|
|
579
|
+
});
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
251
582
|
res.status(500).json({ error: error instanceof Error ? error.message : "Unknown error" });
|
|
252
583
|
}
|
|
253
584
|
});
|
|
@@ -296,26 +627,27 @@ var TOOL_HANDLERS;
|
|
|
296
627
|
var init_http_server = __esm({
|
|
297
628
|
"../core/dist/http-server.js"() {
|
|
298
629
|
"use strict";
|
|
630
|
+
init_bridge_service();
|
|
299
631
|
TOOL_HANDLERS = {
|
|
300
|
-
get_file_tree: (tools, body) => tools.getFileTree(body.path),
|
|
301
|
-
search_files: (tools, body) => tools.searchFiles(body.query, body.searchType),
|
|
302
|
-
get_place_info: (tools) => tools.getPlaceInfo(),
|
|
303
|
-
get_services: (tools, body) => tools.getServices(body.serviceName),
|
|
304
|
-
search_objects: (tools, body) => tools.searchObjects(body.query, body.searchType, body.propertyName),
|
|
305
|
-
get_instance_properties: (tools, body) => tools.getInstanceProperties(body.instancePath, body.excludeSource),
|
|
306
|
-
get_instance_children: (tools, body) => tools.getInstanceChildren(body.instancePath),
|
|
307
|
-
search_by_property: (tools, body) => tools.searchByProperty(body.propertyName, body.propertyValue),
|
|
308
|
-
get_class_info: (tools, body) => tools.getClassInfo(body.className),
|
|
309
|
-
get_project_structure: (tools, body) => tools.getProjectStructure(body.path, body.maxDepth, body.scriptsOnly),
|
|
310
|
-
set_property: (tools, body) => tools.setProperty(body.instancePath, body.propertyName, body.propertyValue),
|
|
311
|
-
set_properties: (tools, body) => tools.setProperties(body.instancePath, body.properties),
|
|
312
|
-
mass_set_property: (tools, body) => tools.massSetProperty(body.paths, body.propertyName, body.propertyValue),
|
|
313
|
-
mass_get_property: (tools, body) => tools.massGetProperty(body.paths, body.propertyName),
|
|
314
|
-
create_object: (tools, body) => tools.createObject(body.className, body.parent, body.name, body.properties),
|
|
315
|
-
mass_create_objects: (tools, body) => tools.massCreateObjects(body.objects),
|
|
316
|
-
delete_object: (tools, body) => tools.deleteObject(body.instancePath),
|
|
317
|
-
smart_duplicate: (tools, body) => tools.smartDuplicate(body.instancePath, body.count, body.options),
|
|
318
|
-
mass_duplicate: (tools, body) => tools.massDuplicate(body.duplications),
|
|
632
|
+
get_file_tree: (tools, body) => tools.getFileTree(body.path, body.instance_id),
|
|
633
|
+
search_files: (tools, body) => tools.searchFiles(body.query, body.searchType, body.instance_id),
|
|
634
|
+
get_place_info: (tools, body) => tools.getPlaceInfo(body.instance_id),
|
|
635
|
+
get_services: (tools, body) => tools.getServices(body.serviceName, body.instance_id),
|
|
636
|
+
search_objects: (tools, body) => tools.searchObjects(body.query, body.searchType, body.propertyName, body.instance_id),
|
|
637
|
+
get_instance_properties: (tools, body) => tools.getInstanceProperties(body.instancePath, body.excludeSource, body.instance_id),
|
|
638
|
+
get_instance_children: (tools, body) => tools.getInstanceChildren(body.instancePath, body.instance_id),
|
|
639
|
+
search_by_property: (tools, body) => tools.searchByProperty(body.propertyName, body.propertyValue, body.instance_id),
|
|
640
|
+
get_class_info: (tools, body) => tools.getClassInfo(body.className, body.instance_id),
|
|
641
|
+
get_project_structure: (tools, body) => tools.getProjectStructure(body.path, body.maxDepth, body.scriptsOnly, body.instance_id),
|
|
642
|
+
set_property: (tools, body) => tools.setProperty(body.instancePath, body.propertyName, body.propertyValue, body.instance_id),
|
|
643
|
+
set_properties: (tools, body) => tools.setProperties(body.instancePath, body.properties, body.instance_id),
|
|
644
|
+
mass_set_property: (tools, body) => tools.massSetProperty(body.paths, body.propertyName, body.propertyValue, body.instance_id),
|
|
645
|
+
mass_get_property: (tools, body) => tools.massGetProperty(body.paths, body.propertyName, body.instance_id),
|
|
646
|
+
create_object: (tools, body) => tools.createObject(body.className, body.parent, body.name, body.properties, body.instance_id),
|
|
647
|
+
mass_create_objects: (tools, body) => tools.massCreateObjects(body.objects, body.instance_id),
|
|
648
|
+
delete_object: (tools, body) => tools.deleteObject(body.instancePath, body.instance_id),
|
|
649
|
+
smart_duplicate: (tools, body) => tools.smartDuplicate(body.instancePath, body.count, body.options, body.instance_id),
|
|
650
|
+
mass_duplicate: (tools, body) => tools.massDuplicate(body.duplications, body.instance_id),
|
|
319
651
|
grep_scripts: (tools, body) => tools.grepScripts(body.pattern, {
|
|
320
652
|
caseSensitive: body.caseSensitive,
|
|
321
653
|
usePattern: body.usePattern,
|
|
@@ -325,56 +657,56 @@ var init_http_server = __esm({
|
|
|
325
657
|
filesOnly: body.filesOnly,
|
|
326
658
|
path: body.path,
|
|
327
659
|
classFilter: body.classFilter
|
|
328
|
-
}),
|
|
329
|
-
get_script_source: (tools, body) => tools.getScriptSource(body.instancePath, body.startLine, body.endLine),
|
|
330
|
-
set_script_source: (tools, body) => tools.setScriptSource(body.instancePath, body.source),
|
|
331
|
-
edit_script_lines: (tools, body) => tools.editScriptLines(body.instancePath, body.old_string, body.new_string, body.startLine),
|
|
332
|
-
insert_script_lines: (tools, body) => tools.insertScriptLines(body.instancePath, body.afterLine, body.newContent),
|
|
333
|
-
delete_script_lines: (tools, body) => tools.deleteScriptLines(body.instancePath, body.startLine, body.endLine),
|
|
334
|
-
set_attribute: (tools, body) => tools.setAttribute(body.instancePath, body.attributeName, body.attributeValue, body.valueType),
|
|
335
|
-
get_attributes: (tools, body) => tools.getAttributes(body.instancePath),
|
|
336
|
-
delete_attribute: (tools, body) => tools.deleteAttribute(body.instancePath, body.attributeName),
|
|
337
|
-
get_tags: (tools, body) => tools.getTags(body.instancePath),
|
|
338
|
-
add_tag: (tools, body) => tools.addTag(body.instancePath, body.tagName),
|
|
339
|
-
remove_tag: (tools, body) => tools.removeTag(body.instancePath, body.tagName),
|
|
340
|
-
get_tagged: (tools, body) => tools.getTagged(body.tagName),
|
|
341
|
-
get_selection: (tools) => tools.getSelection(),
|
|
342
|
-
execute_luau: (tools, body) => tools.executeLuau(body.code, body.target),
|
|
343
|
-
eval_server_runtime: (tools, body) => tools.evalServerRuntime(body.code),
|
|
344
|
-
eval_client_runtime: (tools, body) => tools.evalClientRuntime(body.code, body.target),
|
|
345
|
-
start_playtest: (tools, body) => tools.startPlaytest(body.mode, body.numPlayers),
|
|
346
|
-
stop_playtest: (tools) => tools.stopPlaytest(),
|
|
347
|
-
get_playtest_output: (tools, body) => tools.getPlaytestOutput(body.target),
|
|
348
|
-
get_runtime_logs: (tools, body) => tools.getRuntimeLogs(body.target, body.since, body.tail, body.filter),
|
|
660
|
+
}, body.instance_id),
|
|
661
|
+
get_script_source: (tools, body) => tools.getScriptSource(body.instancePath, body.startLine, body.endLine, body.instance_id),
|
|
662
|
+
set_script_source: (tools, body) => tools.setScriptSource(body.instancePath, body.source, body.instance_id),
|
|
663
|
+
edit_script_lines: (tools, body) => tools.editScriptLines(body.instancePath, body.old_string, body.new_string, body.startLine, body.instance_id),
|
|
664
|
+
insert_script_lines: (tools, body) => tools.insertScriptLines(body.instancePath, body.afterLine, body.newContent, body.instance_id),
|
|
665
|
+
delete_script_lines: (tools, body) => tools.deleteScriptLines(body.instancePath, body.startLine, body.endLine, body.instance_id),
|
|
666
|
+
set_attribute: (tools, body) => tools.setAttribute(body.instancePath, body.attributeName, body.attributeValue, body.valueType, body.instance_id),
|
|
667
|
+
get_attributes: (tools, body) => tools.getAttributes(body.instancePath, body.instance_id),
|
|
668
|
+
delete_attribute: (tools, body) => tools.deleteAttribute(body.instancePath, body.attributeName, body.instance_id),
|
|
669
|
+
get_tags: (tools, body) => tools.getTags(body.instancePath, body.instance_id),
|
|
670
|
+
add_tag: (tools, body) => tools.addTag(body.instancePath, body.tagName, body.instance_id),
|
|
671
|
+
remove_tag: (tools, body) => tools.removeTag(body.instancePath, body.tagName, body.instance_id),
|
|
672
|
+
get_tagged: (tools, body) => tools.getTagged(body.tagName, body.instance_id),
|
|
673
|
+
get_selection: (tools, body) => tools.getSelection(body.instance_id),
|
|
674
|
+
execute_luau: (tools, body) => tools.executeLuau(body.code, body.target, body.instance_id),
|
|
675
|
+
eval_server_runtime: (tools, body) => tools.evalServerRuntime(body.code, body.instance_id),
|
|
676
|
+
eval_client_runtime: (tools, body) => tools.evalClientRuntime(body.code, body.target, body.instance_id),
|
|
677
|
+
start_playtest: (tools, body) => tools.startPlaytest(body.mode, body.numPlayers, body.instance_id),
|
|
678
|
+
stop_playtest: (tools, body) => tools.stopPlaytest(body.instance_id),
|
|
679
|
+
get_playtest_output: (tools, body) => tools.getPlaytestOutput(body.target, body.instance_id),
|
|
680
|
+
get_runtime_logs: (tools, body) => tools.getRuntimeLogs(body.target, body.since, body.tail, body.filter, body.instance_id),
|
|
349
681
|
get_connected_instances: (tools) => tools.getConnectedInstances(),
|
|
350
|
-
export_build: (tools, body) => tools.exportBuild(body.instancePath, body.outputId, body.style),
|
|
682
|
+
export_build: (tools, body) => tools.exportBuild(body.instancePath, body.outputId, body.style, body.instance_id),
|
|
351
683
|
create_build: (tools, body) => tools.createBuild(body.id, body.style, body.palette, body.parts, body.bounds),
|
|
352
684
|
generate_build: (tools, body) => tools.generateBuild(body.id, body.style, body.palette, body.code, body.seed),
|
|
353
|
-
import_build: (tools, body) => tools.importBuild(body.buildData, body.targetPath, body.position),
|
|
685
|
+
import_build: (tools, body) => tools.importBuild(body.buildData, body.targetPath, body.position, body.instance_id),
|
|
354
686
|
list_library: (tools, body) => tools.listLibrary(body.style),
|
|
355
|
-
search_materials: (tools, body) => tools.searchMaterials(body.query, body.maxResults),
|
|
687
|
+
search_materials: (tools, body) => tools.searchMaterials(body.query, body.maxResults, body.instance_id),
|
|
356
688
|
get_build: (tools, body) => tools.getBuild(body.id),
|
|
357
|
-
import_scene: (tools, body) => tools.importScene(body.sceneData, body.targetPath),
|
|
358
|
-
undo: (tools) => tools.undo(),
|
|
359
|
-
redo: (tools) => tools.redo(),
|
|
689
|
+
import_scene: (tools, body) => tools.importScene(body.sceneData, body.targetPath, body.instance_id),
|
|
690
|
+
undo: (tools, body) => tools.undo(body.instance_id),
|
|
691
|
+
redo: (tools, body) => tools.redo(body.instance_id),
|
|
360
692
|
search_assets: (tools, body) => tools.searchAssets(body.assetType, body.query, body.maxResults, body.sortBy, body.verifiedCreatorsOnly),
|
|
361
693
|
get_asset_details: (tools, body) => tools.getAssetDetails(body.assetId),
|
|
362
694
|
get_asset_thumbnail: (tools, body) => tools.getAssetThumbnail(body.assetId, body.size),
|
|
363
|
-
insert_asset: (tools, body) => tools.insertAsset(body.assetId, body.parentPath, body.position),
|
|
364
|
-
preview_asset: (tools, body) => tools.previewAsset(body.assetId, body.includeProperties, body.maxDepth),
|
|
695
|
+
insert_asset: (tools, body) => tools.insertAsset(body.assetId, body.parentPath, body.position, body.instance_id),
|
|
696
|
+
preview_asset: (tools, body) => tools.previewAsset(body.assetId, body.includeProperties, body.maxDepth, body.instance_id),
|
|
365
697
|
upload_asset: (tools, body) => tools.uploadAsset(body.filePath, body.assetType, body.displayName, body.description, body.userId, body.groupId),
|
|
366
|
-
clone_object: (tools, body) => tools.cloneObject(body.instancePath, body.targetParentPath),
|
|
367
|
-
get_descendants: (tools, body) => tools.getDescendants(body.instancePath, body.maxDepth, body.classFilter),
|
|
368
|
-
compare_instances: (tools, body) => tools.compareInstances(body.instancePathA, body.instancePathB),
|
|
369
|
-
get_output_log: (tools, body) => tools.getOutputLog(body.maxEntries, body.messageType),
|
|
370
|
-
bulk_set_attributes: (tools, body) => tools.bulkSetAttributes(body.instancePath, body.attributes),
|
|
371
|
-
capture_screenshot: (tools) => tools.captureScreenshot(),
|
|
372
|
-
simulate_mouse_input: (tools, body) => tools.simulateMouseInput(body.action, body.x, body.y, body.button, body.scrollDirection, body.target),
|
|
373
|
-
simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.target),
|
|
374
|
-
character_navigation: (tools, body) => tools.characterNavigation(body.position, body.instancePath, body.waitForCompletion, body.timeout, body.target),
|
|
375
|
-
get_memory_breakdown: (tools, body) => tools.getMemoryBreakdown(body.target, body.tags),
|
|
376
|
-
export_rbxm: (tools, body) => tools.exportRbxm(body.instance_paths, body.output_path, body.target),
|
|
377
|
-
import_rbxm: (tools, body) => tools.importRbxm(body.source, body.parent_path, body.target),
|
|
698
|
+
clone_object: (tools, body) => tools.cloneObject(body.instancePath, body.targetParentPath, body.instance_id),
|
|
699
|
+
get_descendants: (tools, body) => tools.getDescendants(body.instancePath, body.maxDepth, body.classFilter, body.instance_id),
|
|
700
|
+
compare_instances: (tools, body) => tools.compareInstances(body.instancePathA, body.instancePathB, body.instance_id),
|
|
701
|
+
get_output_log: (tools, body) => tools.getOutputLog(body.maxEntries, body.messageType, body.instance_id),
|
|
702
|
+
bulk_set_attributes: (tools, body) => tools.bulkSetAttributes(body.instancePath, body.attributes, body.instance_id),
|
|
703
|
+
capture_screenshot: (tools, body) => tools.captureScreenshot(body.instance_id, body.format, body.quality),
|
|
704
|
+
simulate_mouse_input: (tools, body) => tools.simulateMouseInput(body.action, body.x, body.y, body.button, body.scrollDirection, body.target, body.instance_id),
|
|
705
|
+
simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.text, body.target, body.instance_id),
|
|
706
|
+
character_navigation: (tools, body) => tools.characterNavigation(body.position, body.instancePath, body.waitForCompletion, body.timeout, body.target, body.instance_id),
|
|
707
|
+
get_memory_breakdown: (tools, body) => tools.getMemoryBreakdown(body.target, body.tags, body.instance_id),
|
|
708
|
+
export_rbxm: (tools, body) => tools.exportRbxm(body.instance_paths, body.output_path, body.target, body.instance_id),
|
|
709
|
+
import_rbxm: (tools, body) => tools.importRbxm(body.source, body.parent_path, body.target, body.instance_id),
|
|
378
710
|
find_and_replace_in_scripts: (tools, body) => tools.findAndReplaceInScripts(body.pattern, body.replacement, {
|
|
379
711
|
caseSensitive: body.caseSensitive,
|
|
380
712
|
usePattern: body.usePattern,
|
|
@@ -382,7 +714,7 @@ var init_http_server = __esm({
|
|
|
382
714
|
classFilter: body.classFilter,
|
|
383
715
|
dryRun: body.dryRun,
|
|
384
716
|
maxReplacements: body.maxReplacements
|
|
385
|
-
})
|
|
717
|
+
}, body.instance_id)
|
|
386
718
|
};
|
|
387
719
|
}
|
|
388
720
|
});
|
|
@@ -397,9 +729,9 @@ var init_studio_client = __esm({
|
|
|
397
729
|
constructor(bridge) {
|
|
398
730
|
this.bridge = bridge;
|
|
399
731
|
}
|
|
400
|
-
async request(endpoint, data,
|
|
732
|
+
async request(endpoint, data, targetInstanceId, targetRole) {
|
|
401
733
|
try {
|
|
402
|
-
const response = await this.bridge.sendRequest(endpoint, data,
|
|
734
|
+
const response = await this.bridge.sendRequest(endpoint, data, targetInstanceId, targetRole);
|
|
403
735
|
return response;
|
|
404
736
|
} catch (error) {
|
|
405
737
|
if (error instanceof Error && error.message === "Request timeout") {
|
|
@@ -1136,6 +1468,963 @@ var init_roblox_cookie_client = __esm({
|
|
|
1136
1468
|
}
|
|
1137
1469
|
});
|
|
1138
1470
|
|
|
1471
|
+
// ../core/dist/jpeg-encoder.js
|
|
1472
|
+
function rgbaToJpeg(rgba, width, height, quality = 80) {
|
|
1473
|
+
if (width <= 0 || height <= 0)
|
|
1474
|
+
throw new Error(`Invalid JPEG dimensions: ${width}x${height}`);
|
|
1475
|
+
const expected = width * height * 4;
|
|
1476
|
+
if (rgba.length < expected)
|
|
1477
|
+
throw new Error(`Buffer too small: got ${rgba.length}, need ${expected}`);
|
|
1478
|
+
const encoder = new JpegEncoder(quality);
|
|
1479
|
+
return encoder.encode(rgba, width, height);
|
|
1480
|
+
}
|
|
1481
|
+
var ZIGZAG, STD_DC_LUMINANCE_NRCODES, STD_DC_LUMINANCE_VALUES, STD_AC_LUMINANCE_NRCODES, STD_AC_LUMINANCE_VALUES, STD_DC_CHROMINANCE_NRCODES, STD_DC_CHROMINANCE_VALUES, STD_AC_CHROMINANCE_NRCODES, STD_AC_CHROMINANCE_VALUES, JpegEncoder;
|
|
1482
|
+
var init_jpeg_encoder = __esm({
|
|
1483
|
+
"../core/dist/jpeg-encoder.js"() {
|
|
1484
|
+
"use strict";
|
|
1485
|
+
ZIGZAG = [
|
|
1486
|
+
0,
|
|
1487
|
+
1,
|
|
1488
|
+
5,
|
|
1489
|
+
6,
|
|
1490
|
+
14,
|
|
1491
|
+
15,
|
|
1492
|
+
27,
|
|
1493
|
+
28,
|
|
1494
|
+
2,
|
|
1495
|
+
4,
|
|
1496
|
+
7,
|
|
1497
|
+
13,
|
|
1498
|
+
16,
|
|
1499
|
+
26,
|
|
1500
|
+
29,
|
|
1501
|
+
42,
|
|
1502
|
+
3,
|
|
1503
|
+
8,
|
|
1504
|
+
12,
|
|
1505
|
+
17,
|
|
1506
|
+
25,
|
|
1507
|
+
30,
|
|
1508
|
+
41,
|
|
1509
|
+
43,
|
|
1510
|
+
9,
|
|
1511
|
+
11,
|
|
1512
|
+
18,
|
|
1513
|
+
24,
|
|
1514
|
+
31,
|
|
1515
|
+
40,
|
|
1516
|
+
44,
|
|
1517
|
+
53,
|
|
1518
|
+
10,
|
|
1519
|
+
19,
|
|
1520
|
+
23,
|
|
1521
|
+
32,
|
|
1522
|
+
39,
|
|
1523
|
+
45,
|
|
1524
|
+
52,
|
|
1525
|
+
54,
|
|
1526
|
+
20,
|
|
1527
|
+
22,
|
|
1528
|
+
33,
|
|
1529
|
+
38,
|
|
1530
|
+
46,
|
|
1531
|
+
51,
|
|
1532
|
+
55,
|
|
1533
|
+
60,
|
|
1534
|
+
21,
|
|
1535
|
+
34,
|
|
1536
|
+
37,
|
|
1537
|
+
47,
|
|
1538
|
+
50,
|
|
1539
|
+
56,
|
|
1540
|
+
59,
|
|
1541
|
+
61,
|
|
1542
|
+
35,
|
|
1543
|
+
36,
|
|
1544
|
+
48,
|
|
1545
|
+
49,
|
|
1546
|
+
57,
|
|
1547
|
+
58,
|
|
1548
|
+
62,
|
|
1549
|
+
63
|
|
1550
|
+
];
|
|
1551
|
+
STD_DC_LUMINANCE_NRCODES = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0];
|
|
1552
|
+
STD_DC_LUMINANCE_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
1553
|
+
STD_AC_LUMINANCE_NRCODES = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125];
|
|
1554
|
+
STD_AC_LUMINANCE_VALUES = [
|
|
1555
|
+
1,
|
|
1556
|
+
2,
|
|
1557
|
+
3,
|
|
1558
|
+
0,
|
|
1559
|
+
4,
|
|
1560
|
+
17,
|
|
1561
|
+
5,
|
|
1562
|
+
18,
|
|
1563
|
+
33,
|
|
1564
|
+
49,
|
|
1565
|
+
65,
|
|
1566
|
+
6,
|
|
1567
|
+
19,
|
|
1568
|
+
81,
|
|
1569
|
+
97,
|
|
1570
|
+
7,
|
|
1571
|
+
34,
|
|
1572
|
+
113,
|
|
1573
|
+
20,
|
|
1574
|
+
50,
|
|
1575
|
+
129,
|
|
1576
|
+
145,
|
|
1577
|
+
161,
|
|
1578
|
+
8,
|
|
1579
|
+
35,
|
|
1580
|
+
66,
|
|
1581
|
+
177,
|
|
1582
|
+
193,
|
|
1583
|
+
21,
|
|
1584
|
+
82,
|
|
1585
|
+
209,
|
|
1586
|
+
240,
|
|
1587
|
+
36,
|
|
1588
|
+
51,
|
|
1589
|
+
98,
|
|
1590
|
+
114,
|
|
1591
|
+
130,
|
|
1592
|
+
9,
|
|
1593
|
+
10,
|
|
1594
|
+
22,
|
|
1595
|
+
23,
|
|
1596
|
+
24,
|
|
1597
|
+
25,
|
|
1598
|
+
26,
|
|
1599
|
+
37,
|
|
1600
|
+
38,
|
|
1601
|
+
39,
|
|
1602
|
+
40,
|
|
1603
|
+
41,
|
|
1604
|
+
42,
|
|
1605
|
+
52,
|
|
1606
|
+
53,
|
|
1607
|
+
54,
|
|
1608
|
+
55,
|
|
1609
|
+
56,
|
|
1610
|
+
57,
|
|
1611
|
+
58,
|
|
1612
|
+
67,
|
|
1613
|
+
68,
|
|
1614
|
+
69,
|
|
1615
|
+
70,
|
|
1616
|
+
71,
|
|
1617
|
+
72,
|
|
1618
|
+
73,
|
|
1619
|
+
74,
|
|
1620
|
+
83,
|
|
1621
|
+
84,
|
|
1622
|
+
85,
|
|
1623
|
+
86,
|
|
1624
|
+
87,
|
|
1625
|
+
88,
|
|
1626
|
+
89,
|
|
1627
|
+
90,
|
|
1628
|
+
99,
|
|
1629
|
+
100,
|
|
1630
|
+
101,
|
|
1631
|
+
102,
|
|
1632
|
+
103,
|
|
1633
|
+
104,
|
|
1634
|
+
105,
|
|
1635
|
+
106,
|
|
1636
|
+
115,
|
|
1637
|
+
116,
|
|
1638
|
+
117,
|
|
1639
|
+
118,
|
|
1640
|
+
119,
|
|
1641
|
+
120,
|
|
1642
|
+
121,
|
|
1643
|
+
122,
|
|
1644
|
+
131,
|
|
1645
|
+
132,
|
|
1646
|
+
133,
|
|
1647
|
+
134,
|
|
1648
|
+
135,
|
|
1649
|
+
136,
|
|
1650
|
+
137,
|
|
1651
|
+
138,
|
|
1652
|
+
146,
|
|
1653
|
+
147,
|
|
1654
|
+
148,
|
|
1655
|
+
149,
|
|
1656
|
+
150,
|
|
1657
|
+
151,
|
|
1658
|
+
152,
|
|
1659
|
+
153,
|
|
1660
|
+
154,
|
|
1661
|
+
162,
|
|
1662
|
+
163,
|
|
1663
|
+
164,
|
|
1664
|
+
165,
|
|
1665
|
+
166,
|
|
1666
|
+
167,
|
|
1667
|
+
168,
|
|
1668
|
+
169,
|
|
1669
|
+
170,
|
|
1670
|
+
178,
|
|
1671
|
+
179,
|
|
1672
|
+
180,
|
|
1673
|
+
181,
|
|
1674
|
+
182,
|
|
1675
|
+
183,
|
|
1676
|
+
184,
|
|
1677
|
+
185,
|
|
1678
|
+
186,
|
|
1679
|
+
194,
|
|
1680
|
+
195,
|
|
1681
|
+
196,
|
|
1682
|
+
197,
|
|
1683
|
+
198,
|
|
1684
|
+
199,
|
|
1685
|
+
200,
|
|
1686
|
+
201,
|
|
1687
|
+
202,
|
|
1688
|
+
210,
|
|
1689
|
+
211,
|
|
1690
|
+
212,
|
|
1691
|
+
213,
|
|
1692
|
+
214,
|
|
1693
|
+
215,
|
|
1694
|
+
216,
|
|
1695
|
+
217,
|
|
1696
|
+
218,
|
|
1697
|
+
225,
|
|
1698
|
+
226,
|
|
1699
|
+
227,
|
|
1700
|
+
228,
|
|
1701
|
+
229,
|
|
1702
|
+
230,
|
|
1703
|
+
231,
|
|
1704
|
+
232,
|
|
1705
|
+
233,
|
|
1706
|
+
234,
|
|
1707
|
+
241,
|
|
1708
|
+
242,
|
|
1709
|
+
243,
|
|
1710
|
+
244,
|
|
1711
|
+
245,
|
|
1712
|
+
246,
|
|
1713
|
+
247,
|
|
1714
|
+
248,
|
|
1715
|
+
249,
|
|
1716
|
+
250
|
|
1717
|
+
];
|
|
1718
|
+
STD_DC_CHROMINANCE_NRCODES = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0];
|
|
1719
|
+
STD_DC_CHROMINANCE_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
1720
|
+
STD_AC_CHROMINANCE_NRCODES = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119];
|
|
1721
|
+
STD_AC_CHROMINANCE_VALUES = [
|
|
1722
|
+
0,
|
|
1723
|
+
1,
|
|
1724
|
+
2,
|
|
1725
|
+
3,
|
|
1726
|
+
17,
|
|
1727
|
+
4,
|
|
1728
|
+
5,
|
|
1729
|
+
33,
|
|
1730
|
+
49,
|
|
1731
|
+
6,
|
|
1732
|
+
18,
|
|
1733
|
+
65,
|
|
1734
|
+
81,
|
|
1735
|
+
7,
|
|
1736
|
+
97,
|
|
1737
|
+
113,
|
|
1738
|
+
19,
|
|
1739
|
+
34,
|
|
1740
|
+
50,
|
|
1741
|
+
129,
|
|
1742
|
+
8,
|
|
1743
|
+
20,
|
|
1744
|
+
66,
|
|
1745
|
+
145,
|
|
1746
|
+
161,
|
|
1747
|
+
177,
|
|
1748
|
+
193,
|
|
1749
|
+
9,
|
|
1750
|
+
35,
|
|
1751
|
+
51,
|
|
1752
|
+
82,
|
|
1753
|
+
240,
|
|
1754
|
+
21,
|
|
1755
|
+
98,
|
|
1756
|
+
114,
|
|
1757
|
+
209,
|
|
1758
|
+
10,
|
|
1759
|
+
22,
|
|
1760
|
+
36,
|
|
1761
|
+
52,
|
|
1762
|
+
225,
|
|
1763
|
+
37,
|
|
1764
|
+
241,
|
|
1765
|
+
23,
|
|
1766
|
+
24,
|
|
1767
|
+
25,
|
|
1768
|
+
26,
|
|
1769
|
+
38,
|
|
1770
|
+
39,
|
|
1771
|
+
40,
|
|
1772
|
+
41,
|
|
1773
|
+
42,
|
|
1774
|
+
53,
|
|
1775
|
+
54,
|
|
1776
|
+
55,
|
|
1777
|
+
56,
|
|
1778
|
+
57,
|
|
1779
|
+
58,
|
|
1780
|
+
67,
|
|
1781
|
+
68,
|
|
1782
|
+
69,
|
|
1783
|
+
70,
|
|
1784
|
+
71,
|
|
1785
|
+
72,
|
|
1786
|
+
73,
|
|
1787
|
+
74,
|
|
1788
|
+
83,
|
|
1789
|
+
84,
|
|
1790
|
+
85,
|
|
1791
|
+
86,
|
|
1792
|
+
87,
|
|
1793
|
+
88,
|
|
1794
|
+
89,
|
|
1795
|
+
90,
|
|
1796
|
+
99,
|
|
1797
|
+
100,
|
|
1798
|
+
101,
|
|
1799
|
+
102,
|
|
1800
|
+
103,
|
|
1801
|
+
104,
|
|
1802
|
+
105,
|
|
1803
|
+
106,
|
|
1804
|
+
115,
|
|
1805
|
+
116,
|
|
1806
|
+
117,
|
|
1807
|
+
118,
|
|
1808
|
+
119,
|
|
1809
|
+
120,
|
|
1810
|
+
121,
|
|
1811
|
+
122,
|
|
1812
|
+
130,
|
|
1813
|
+
131,
|
|
1814
|
+
132,
|
|
1815
|
+
133,
|
|
1816
|
+
134,
|
|
1817
|
+
135,
|
|
1818
|
+
136,
|
|
1819
|
+
137,
|
|
1820
|
+
138,
|
|
1821
|
+
146,
|
|
1822
|
+
147,
|
|
1823
|
+
148,
|
|
1824
|
+
149,
|
|
1825
|
+
150,
|
|
1826
|
+
151,
|
|
1827
|
+
152,
|
|
1828
|
+
153,
|
|
1829
|
+
154,
|
|
1830
|
+
162,
|
|
1831
|
+
163,
|
|
1832
|
+
164,
|
|
1833
|
+
165,
|
|
1834
|
+
166,
|
|
1835
|
+
167,
|
|
1836
|
+
168,
|
|
1837
|
+
169,
|
|
1838
|
+
170,
|
|
1839
|
+
178,
|
|
1840
|
+
179,
|
|
1841
|
+
180,
|
|
1842
|
+
181,
|
|
1843
|
+
182,
|
|
1844
|
+
183,
|
|
1845
|
+
184,
|
|
1846
|
+
185,
|
|
1847
|
+
186,
|
|
1848
|
+
194,
|
|
1849
|
+
195,
|
|
1850
|
+
196,
|
|
1851
|
+
197,
|
|
1852
|
+
198,
|
|
1853
|
+
199,
|
|
1854
|
+
200,
|
|
1855
|
+
201,
|
|
1856
|
+
202,
|
|
1857
|
+
210,
|
|
1858
|
+
211,
|
|
1859
|
+
212,
|
|
1860
|
+
213,
|
|
1861
|
+
214,
|
|
1862
|
+
215,
|
|
1863
|
+
216,
|
|
1864
|
+
217,
|
|
1865
|
+
218,
|
|
1866
|
+
226,
|
|
1867
|
+
227,
|
|
1868
|
+
228,
|
|
1869
|
+
229,
|
|
1870
|
+
230,
|
|
1871
|
+
231,
|
|
1872
|
+
232,
|
|
1873
|
+
233,
|
|
1874
|
+
234,
|
|
1875
|
+
242,
|
|
1876
|
+
243,
|
|
1877
|
+
244,
|
|
1878
|
+
245,
|
|
1879
|
+
246,
|
|
1880
|
+
247,
|
|
1881
|
+
248,
|
|
1882
|
+
249,
|
|
1883
|
+
250
|
|
1884
|
+
];
|
|
1885
|
+
JpegEncoder = class {
|
|
1886
|
+
YTable = new Int32Array(64);
|
|
1887
|
+
UVTable = new Int32Array(64);
|
|
1888
|
+
fdtbl_Y = new Float32Array(64);
|
|
1889
|
+
fdtbl_UV = new Float32Array(64);
|
|
1890
|
+
YDC_HT;
|
|
1891
|
+
UVDC_HT;
|
|
1892
|
+
YAC_HT;
|
|
1893
|
+
UVAC_HT;
|
|
1894
|
+
bitcode = new Array(65535);
|
|
1895
|
+
category = new Int32Array(65535);
|
|
1896
|
+
outputfDCTQuant = new Float32Array(64);
|
|
1897
|
+
DU = new Int32Array(64);
|
|
1898
|
+
// RGB->YUV lookup tables
|
|
1899
|
+
RGB_YUV_TABLE = new Int32Array(2048);
|
|
1900
|
+
byteout = [];
|
|
1901
|
+
bytenew = 0;
|
|
1902
|
+
bytepos = 7;
|
|
1903
|
+
constructor(quality) {
|
|
1904
|
+
this.initHuffmanTbl();
|
|
1905
|
+
this.initCategoryNumber();
|
|
1906
|
+
this.initRGBYUVTable();
|
|
1907
|
+
this.setQuality(quality);
|
|
1908
|
+
}
|
|
1909
|
+
initQuantTables(sf) {
|
|
1910
|
+
const YQT = [
|
|
1911
|
+
16,
|
|
1912
|
+
11,
|
|
1913
|
+
10,
|
|
1914
|
+
16,
|
|
1915
|
+
24,
|
|
1916
|
+
40,
|
|
1917
|
+
51,
|
|
1918
|
+
61,
|
|
1919
|
+
12,
|
|
1920
|
+
12,
|
|
1921
|
+
14,
|
|
1922
|
+
19,
|
|
1923
|
+
26,
|
|
1924
|
+
58,
|
|
1925
|
+
60,
|
|
1926
|
+
55,
|
|
1927
|
+
14,
|
|
1928
|
+
13,
|
|
1929
|
+
16,
|
|
1930
|
+
24,
|
|
1931
|
+
40,
|
|
1932
|
+
57,
|
|
1933
|
+
69,
|
|
1934
|
+
56,
|
|
1935
|
+
14,
|
|
1936
|
+
17,
|
|
1937
|
+
22,
|
|
1938
|
+
29,
|
|
1939
|
+
51,
|
|
1940
|
+
87,
|
|
1941
|
+
80,
|
|
1942
|
+
62,
|
|
1943
|
+
18,
|
|
1944
|
+
22,
|
|
1945
|
+
37,
|
|
1946
|
+
56,
|
|
1947
|
+
68,
|
|
1948
|
+
109,
|
|
1949
|
+
103,
|
|
1950
|
+
77,
|
|
1951
|
+
24,
|
|
1952
|
+
35,
|
|
1953
|
+
55,
|
|
1954
|
+
64,
|
|
1955
|
+
81,
|
|
1956
|
+
104,
|
|
1957
|
+
113,
|
|
1958
|
+
92,
|
|
1959
|
+
49,
|
|
1960
|
+
64,
|
|
1961
|
+
78,
|
|
1962
|
+
87,
|
|
1963
|
+
103,
|
|
1964
|
+
121,
|
|
1965
|
+
120,
|
|
1966
|
+
101,
|
|
1967
|
+
72,
|
|
1968
|
+
92,
|
|
1969
|
+
95,
|
|
1970
|
+
98,
|
|
1971
|
+
112,
|
|
1972
|
+
100,
|
|
1973
|
+
103,
|
|
1974
|
+
99
|
|
1975
|
+
];
|
|
1976
|
+
for (let i = 0; i < 64; i++) {
|
|
1977
|
+
let t = Math.floor((YQT[i] * sf + 50) / 100);
|
|
1978
|
+
if (t < 1)
|
|
1979
|
+
t = 1;
|
|
1980
|
+
else if (t > 255)
|
|
1981
|
+
t = 255;
|
|
1982
|
+
this.YTable[ZIGZAG[i]] = t;
|
|
1983
|
+
}
|
|
1984
|
+
const UVQT = [
|
|
1985
|
+
17,
|
|
1986
|
+
18,
|
|
1987
|
+
24,
|
|
1988
|
+
47,
|
|
1989
|
+
99,
|
|
1990
|
+
99,
|
|
1991
|
+
99,
|
|
1992
|
+
99,
|
|
1993
|
+
18,
|
|
1994
|
+
21,
|
|
1995
|
+
26,
|
|
1996
|
+
66,
|
|
1997
|
+
99,
|
|
1998
|
+
99,
|
|
1999
|
+
99,
|
|
2000
|
+
99,
|
|
2001
|
+
24,
|
|
2002
|
+
26,
|
|
2003
|
+
56,
|
|
2004
|
+
99,
|
|
2005
|
+
99,
|
|
2006
|
+
99,
|
|
2007
|
+
99,
|
|
2008
|
+
99,
|
|
2009
|
+
47,
|
|
2010
|
+
66,
|
|
2011
|
+
99,
|
|
2012
|
+
99,
|
|
2013
|
+
99,
|
|
2014
|
+
99,
|
|
2015
|
+
99,
|
|
2016
|
+
99,
|
|
2017
|
+
99,
|
|
2018
|
+
99,
|
|
2019
|
+
99,
|
|
2020
|
+
99,
|
|
2021
|
+
99,
|
|
2022
|
+
99,
|
|
2023
|
+
99,
|
|
2024
|
+
99,
|
|
2025
|
+
99,
|
|
2026
|
+
99,
|
|
2027
|
+
99,
|
|
2028
|
+
99,
|
|
2029
|
+
99,
|
|
2030
|
+
99,
|
|
2031
|
+
99,
|
|
2032
|
+
99,
|
|
2033
|
+
99,
|
|
2034
|
+
99,
|
|
2035
|
+
99,
|
|
2036
|
+
99,
|
|
2037
|
+
99,
|
|
2038
|
+
99,
|
|
2039
|
+
99,
|
|
2040
|
+
99,
|
|
2041
|
+
99,
|
|
2042
|
+
99,
|
|
2043
|
+
99,
|
|
2044
|
+
99,
|
|
2045
|
+
99,
|
|
2046
|
+
99,
|
|
2047
|
+
99,
|
|
2048
|
+
99
|
|
2049
|
+
];
|
|
2050
|
+
for (let j = 0; j < 64; j++) {
|
|
2051
|
+
let u = Math.floor((UVQT[j] * sf + 50) / 100);
|
|
2052
|
+
if (u < 1)
|
|
2053
|
+
u = 1;
|
|
2054
|
+
else if (u > 255)
|
|
2055
|
+
u = 255;
|
|
2056
|
+
this.UVTable[ZIGZAG[j]] = u;
|
|
2057
|
+
}
|
|
2058
|
+
const aasf = [
|
|
2059
|
+
1,
|
|
2060
|
+
1.387039845,
|
|
2061
|
+
1.306562965,
|
|
2062
|
+
1.175875602,
|
|
2063
|
+
1,
|
|
2064
|
+
0.785694958,
|
|
2065
|
+
0.5411961,
|
|
2066
|
+
0.275899379
|
|
2067
|
+
];
|
|
2068
|
+
let k = 0;
|
|
2069
|
+
for (let row = 0; row < 8; row++) {
|
|
2070
|
+
for (let col = 0; col < 8; col++) {
|
|
2071
|
+
this.fdtbl_Y[k] = 1 / (this.YTable[ZIGZAG[k]] * aasf[row] * aasf[col] * 8);
|
|
2072
|
+
this.fdtbl_UV[k] = 1 / (this.UVTable[ZIGZAG[k]] * aasf[row] * aasf[col] * 8);
|
|
2073
|
+
k++;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
computeHuffmanTbl(nrcodes, std_table) {
|
|
2078
|
+
let codevalue = 0;
|
|
2079
|
+
let pos_in_table = 0;
|
|
2080
|
+
const HT = [];
|
|
2081
|
+
for (let k = 1; k <= 16; k++) {
|
|
2082
|
+
for (let j = 1; j <= nrcodes[k]; j++) {
|
|
2083
|
+
HT[std_table[pos_in_table]] = { code: codevalue, length: k };
|
|
2084
|
+
pos_in_table++;
|
|
2085
|
+
codevalue++;
|
|
2086
|
+
}
|
|
2087
|
+
codevalue *= 2;
|
|
2088
|
+
}
|
|
2089
|
+
return HT;
|
|
2090
|
+
}
|
|
2091
|
+
initHuffmanTbl() {
|
|
2092
|
+
this.YDC_HT = this.computeHuffmanTbl(STD_DC_LUMINANCE_NRCODES, STD_DC_LUMINANCE_VALUES);
|
|
2093
|
+
this.UVDC_HT = this.computeHuffmanTbl(STD_DC_CHROMINANCE_NRCODES, STD_DC_CHROMINANCE_VALUES);
|
|
2094
|
+
this.YAC_HT = this.computeHuffmanTbl(STD_AC_LUMINANCE_NRCODES, STD_AC_LUMINANCE_VALUES);
|
|
2095
|
+
this.UVAC_HT = this.computeHuffmanTbl(STD_AC_CHROMINANCE_NRCODES, STD_AC_CHROMINANCE_VALUES);
|
|
2096
|
+
}
|
|
2097
|
+
initCategoryNumber() {
|
|
2098
|
+
let nrlower = 1;
|
|
2099
|
+
let nrupper = 2;
|
|
2100
|
+
for (let cat = 1; cat <= 15; cat++) {
|
|
2101
|
+
for (let nr = nrlower; nr < nrupper; nr++) {
|
|
2102
|
+
this.category[32767 + nr] = cat;
|
|
2103
|
+
this.bitcode[32767 + nr] = { length: cat, code: nr };
|
|
2104
|
+
}
|
|
2105
|
+
for (let nrneg = -(nrupper - 1); nrneg <= -nrlower; nrneg++) {
|
|
2106
|
+
this.category[32767 + nrneg] = cat;
|
|
2107
|
+
this.bitcode[32767 + nrneg] = { length: cat, code: nrupper - 1 + nrneg };
|
|
2108
|
+
}
|
|
2109
|
+
nrlower <<= 1;
|
|
2110
|
+
nrupper <<= 1;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
initRGBYUVTable() {
|
|
2114
|
+
for (let i = 0; i < 256; i++) {
|
|
2115
|
+
this.RGB_YUV_TABLE[i] = 19595 * i;
|
|
2116
|
+
this.RGB_YUV_TABLE[i + 256 >> 0] = 38470 * i;
|
|
2117
|
+
this.RGB_YUV_TABLE[i + 512 >> 0] = 7471 * i + 32768;
|
|
2118
|
+
this.RGB_YUV_TABLE[i + 768 >> 0] = -11059 * i;
|
|
2119
|
+
this.RGB_YUV_TABLE[i + 1024 >> 0] = -21709 * i;
|
|
2120
|
+
this.RGB_YUV_TABLE[i + 1280 >> 0] = 32768 * i + 8421375;
|
|
2121
|
+
this.RGB_YUV_TABLE[i + 1536 >> 0] = -27439 * i;
|
|
2122
|
+
this.RGB_YUV_TABLE[i + 1792 >> 0] = -5329 * i;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
setQuality(quality) {
|
|
2126
|
+
let q = quality;
|
|
2127
|
+
if (q <= 0)
|
|
2128
|
+
q = 1;
|
|
2129
|
+
if (q > 100)
|
|
2130
|
+
q = 100;
|
|
2131
|
+
const sf = q < 50 ? Math.floor(5e3 / q) : Math.floor(200 - q * 2);
|
|
2132
|
+
this.initQuantTables(sf);
|
|
2133
|
+
}
|
|
2134
|
+
writeBits(bs) {
|
|
2135
|
+
const value = bs.code;
|
|
2136
|
+
let posval = bs.length - 1;
|
|
2137
|
+
while (posval >= 0) {
|
|
2138
|
+
if (value & 1 << posval) {
|
|
2139
|
+
this.bytenew |= 1 << this.bytepos;
|
|
2140
|
+
}
|
|
2141
|
+
posval--;
|
|
2142
|
+
this.bytepos--;
|
|
2143
|
+
if (this.bytepos < 0) {
|
|
2144
|
+
if (this.bytenew === 255) {
|
|
2145
|
+
this.writeByte(255);
|
|
2146
|
+
this.writeByte(0);
|
|
2147
|
+
} else {
|
|
2148
|
+
this.writeByte(this.bytenew);
|
|
2149
|
+
}
|
|
2150
|
+
this.bytepos = 7;
|
|
2151
|
+
this.bytenew = 0;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
writeByte(value) {
|
|
2156
|
+
this.byteout.push(value & 255);
|
|
2157
|
+
}
|
|
2158
|
+
writeWord(value) {
|
|
2159
|
+
this.writeByte(value >> 8 & 255);
|
|
2160
|
+
this.writeByte(value & 255);
|
|
2161
|
+
}
|
|
2162
|
+
fDCTQuant(data, fdtbl) {
|
|
2163
|
+
let d0, d1, d2, d3, d4, d5, d6, d7;
|
|
2164
|
+
let dataOff = 0;
|
|
2165
|
+
const I8 = 8;
|
|
2166
|
+
const I64 = 64;
|
|
2167
|
+
for (let i = 0; i < I8; ++i) {
|
|
2168
|
+
d0 = data[dataOff];
|
|
2169
|
+
d1 = data[dataOff + 1];
|
|
2170
|
+
d2 = data[dataOff + 2];
|
|
2171
|
+
d3 = data[dataOff + 3];
|
|
2172
|
+
d4 = data[dataOff + 4];
|
|
2173
|
+
d5 = data[dataOff + 5];
|
|
2174
|
+
d6 = data[dataOff + 6];
|
|
2175
|
+
d7 = data[dataOff + 7];
|
|
2176
|
+
const tmp0 = d0 + d7;
|
|
2177
|
+
const tmp7 = d0 - d7;
|
|
2178
|
+
const tmp1 = d1 + d6;
|
|
2179
|
+
const tmp6 = d1 - d6;
|
|
2180
|
+
const tmp2 = d2 + d5;
|
|
2181
|
+
const tmp5 = d2 - d5;
|
|
2182
|
+
const tmp3 = d3 + d4;
|
|
2183
|
+
const tmp4 = d3 - d4;
|
|
2184
|
+
let tmp10 = tmp0 + tmp3;
|
|
2185
|
+
const tmp13 = tmp0 - tmp3;
|
|
2186
|
+
let tmp11 = tmp1 + tmp2;
|
|
2187
|
+
let tmp12 = tmp1 - tmp2;
|
|
2188
|
+
data[dataOff] = tmp10 + tmp11;
|
|
2189
|
+
data[dataOff + 4] = tmp10 - tmp11;
|
|
2190
|
+
const z1 = (tmp12 + tmp13) * 0.707106781;
|
|
2191
|
+
data[dataOff + 2] = tmp13 + z1;
|
|
2192
|
+
data[dataOff + 6] = tmp13 - z1;
|
|
2193
|
+
tmp10 = tmp4 + tmp5;
|
|
2194
|
+
tmp11 = tmp5 + tmp6;
|
|
2195
|
+
tmp12 = tmp6 + tmp7;
|
|
2196
|
+
const z5 = (tmp10 - tmp12) * 0.382683433;
|
|
2197
|
+
const z2 = 0.5411961 * tmp10 + z5;
|
|
2198
|
+
const z4 = 1.306562965 * tmp12 + z5;
|
|
2199
|
+
const z3 = tmp11 * 0.707106781;
|
|
2200
|
+
const z11 = tmp7 + z3;
|
|
2201
|
+
const z13 = tmp7 - z3;
|
|
2202
|
+
data[dataOff + 5] = z13 + z2;
|
|
2203
|
+
data[dataOff + 3] = z13 - z2;
|
|
2204
|
+
data[dataOff + 1] = z11 + z4;
|
|
2205
|
+
data[dataOff + 7] = z11 - z4;
|
|
2206
|
+
dataOff += 8;
|
|
2207
|
+
}
|
|
2208
|
+
dataOff = 0;
|
|
2209
|
+
for (let i = 0; i < I8; ++i) {
|
|
2210
|
+
d0 = data[dataOff];
|
|
2211
|
+
d1 = data[dataOff + 8];
|
|
2212
|
+
d2 = data[dataOff + 16];
|
|
2213
|
+
d3 = data[dataOff + 24];
|
|
2214
|
+
d4 = data[dataOff + 32];
|
|
2215
|
+
d5 = data[dataOff + 40];
|
|
2216
|
+
d6 = data[dataOff + 48];
|
|
2217
|
+
d7 = data[dataOff + 56];
|
|
2218
|
+
const tmp0p2 = d0 + d7;
|
|
2219
|
+
const tmp7p2 = d0 - d7;
|
|
2220
|
+
const tmp1p2 = d1 + d6;
|
|
2221
|
+
const tmp6p2 = d1 - d6;
|
|
2222
|
+
const tmp2p2 = d2 + d5;
|
|
2223
|
+
const tmp5p2 = d2 - d5;
|
|
2224
|
+
const tmp3p2 = d3 + d4;
|
|
2225
|
+
const tmp4p2 = d3 - d4;
|
|
2226
|
+
let tmp10p2 = tmp0p2 + tmp3p2;
|
|
2227
|
+
const tmp13p2 = tmp0p2 - tmp3p2;
|
|
2228
|
+
let tmp11p2 = tmp1p2 + tmp2p2;
|
|
2229
|
+
let tmp12p2 = tmp1p2 - tmp2p2;
|
|
2230
|
+
data[dataOff] = tmp10p2 + tmp11p2;
|
|
2231
|
+
data[dataOff + 32] = tmp10p2 - tmp11p2;
|
|
2232
|
+
const z1p2 = (tmp12p2 + tmp13p2) * 0.707106781;
|
|
2233
|
+
data[dataOff + 16] = tmp13p2 + z1p2;
|
|
2234
|
+
data[dataOff + 48] = tmp13p2 - z1p2;
|
|
2235
|
+
tmp10p2 = tmp4p2 + tmp5p2;
|
|
2236
|
+
tmp11p2 = tmp5p2 + tmp6p2;
|
|
2237
|
+
tmp12p2 = tmp6p2 + tmp7p2;
|
|
2238
|
+
const z5p2 = (tmp10p2 - tmp12p2) * 0.382683433;
|
|
2239
|
+
const z2p2 = 0.5411961 * tmp10p2 + z5p2;
|
|
2240
|
+
const z4p2 = 1.306562965 * tmp12p2 + z5p2;
|
|
2241
|
+
const z3p2 = tmp11p2 * 0.707106781;
|
|
2242
|
+
const z11p2 = tmp7p2 + z3p2;
|
|
2243
|
+
const z13p2 = tmp7p2 - z3p2;
|
|
2244
|
+
data[dataOff + 40] = z13p2 + z2p2;
|
|
2245
|
+
data[dataOff + 24] = z13p2 - z2p2;
|
|
2246
|
+
data[dataOff + 8] = z11p2 + z4p2;
|
|
2247
|
+
data[dataOff + 56] = z11p2 - z4p2;
|
|
2248
|
+
dataOff++;
|
|
2249
|
+
}
|
|
2250
|
+
for (let i = 0; i < I64; ++i) {
|
|
2251
|
+
const fDCTVal = data[i] * fdtbl[i];
|
|
2252
|
+
this.outputfDCTQuant[i] = fDCTVal > 0 ? fDCTVal + 0.5 | 0 : fDCTVal - 0.5 | 0;
|
|
2253
|
+
}
|
|
2254
|
+
return this.outputfDCTQuant;
|
|
2255
|
+
}
|
|
2256
|
+
processDU(CDU, fdtbl, DC, HTDC, HTAC) {
|
|
2257
|
+
const EOB = HTAC[0];
|
|
2258
|
+
const M16zeroes = HTAC[240];
|
|
2259
|
+
let pos;
|
|
2260
|
+
const I16 = 16;
|
|
2261
|
+
const I63 = 63;
|
|
2262
|
+
const I64 = 64;
|
|
2263
|
+
const DU_DCT = this.fDCTQuant(CDU, fdtbl);
|
|
2264
|
+
for (let j = 0; j < I64; ++j) {
|
|
2265
|
+
this.DU[ZIGZAG[j]] = DU_DCT[j];
|
|
2266
|
+
}
|
|
2267
|
+
const Diff = this.DU[0] - DC;
|
|
2268
|
+
DC = this.DU[0];
|
|
2269
|
+
if (Diff === 0) {
|
|
2270
|
+
this.writeBits(HTDC[0]);
|
|
2271
|
+
} else {
|
|
2272
|
+
pos = 32767 + Diff;
|
|
2273
|
+
this.writeBits(HTDC[this.category[pos]]);
|
|
2274
|
+
this.writeBits(this.bitcode[pos]);
|
|
2275
|
+
}
|
|
2276
|
+
let end0pos = 63;
|
|
2277
|
+
for (; end0pos > 0 && this.DU[end0pos] === 0; end0pos--) {
|
|
2278
|
+
}
|
|
2279
|
+
if (end0pos === 0) {
|
|
2280
|
+
this.writeBits(EOB);
|
|
2281
|
+
return DC;
|
|
2282
|
+
}
|
|
2283
|
+
let i = 1;
|
|
2284
|
+
let lng;
|
|
2285
|
+
while (i <= end0pos) {
|
|
2286
|
+
const startpos = i;
|
|
2287
|
+
for (; this.DU[i] === 0 && i <= end0pos; ++i) {
|
|
2288
|
+
}
|
|
2289
|
+
let nrzeroes = i - startpos;
|
|
2290
|
+
if (nrzeroes >= I16) {
|
|
2291
|
+
lng = nrzeroes >> 4;
|
|
2292
|
+
for (let nrmarker = 1; nrmarker <= lng; ++nrmarker)
|
|
2293
|
+
this.writeBits(M16zeroes);
|
|
2294
|
+
nrzeroes = nrzeroes & 15;
|
|
2295
|
+
}
|
|
2296
|
+
pos = 32767 + this.DU[i];
|
|
2297
|
+
this.writeBits(HTAC[(nrzeroes << 4) + this.category[pos]]);
|
|
2298
|
+
this.writeBits(this.bitcode[pos]);
|
|
2299
|
+
i++;
|
|
2300
|
+
}
|
|
2301
|
+
if (end0pos !== I63) {
|
|
2302
|
+
this.writeBits(EOB);
|
|
2303
|
+
}
|
|
2304
|
+
return DC;
|
|
2305
|
+
}
|
|
2306
|
+
encode(rgba, width, height) {
|
|
2307
|
+
this.byteout = [];
|
|
2308
|
+
this.bytenew = 0;
|
|
2309
|
+
this.bytepos = 7;
|
|
2310
|
+
this.writeWord(65496);
|
|
2311
|
+
this.writeWord(65504);
|
|
2312
|
+
this.writeWord(16);
|
|
2313
|
+
this.writeByte(74);
|
|
2314
|
+
this.writeByte(70);
|
|
2315
|
+
this.writeByte(73);
|
|
2316
|
+
this.writeByte(70);
|
|
2317
|
+
this.writeByte(0);
|
|
2318
|
+
this.writeByte(1);
|
|
2319
|
+
this.writeByte(1);
|
|
2320
|
+
this.writeByte(0);
|
|
2321
|
+
this.writeWord(1);
|
|
2322
|
+
this.writeWord(1);
|
|
2323
|
+
this.writeByte(0);
|
|
2324
|
+
this.writeByte(0);
|
|
2325
|
+
this.writeWord(65499);
|
|
2326
|
+
this.writeWord(132);
|
|
2327
|
+
this.writeByte(0);
|
|
2328
|
+
for (let i = 0; i < 64; i++)
|
|
2329
|
+
this.writeByte(this.YTable[i]);
|
|
2330
|
+
this.writeByte(1);
|
|
2331
|
+
for (let j = 0; j < 64; j++)
|
|
2332
|
+
this.writeByte(this.UVTable[j]);
|
|
2333
|
+
this.writeWord(65472);
|
|
2334
|
+
this.writeWord(17);
|
|
2335
|
+
this.writeByte(8);
|
|
2336
|
+
this.writeWord(height);
|
|
2337
|
+
this.writeWord(width);
|
|
2338
|
+
this.writeByte(3);
|
|
2339
|
+
this.writeByte(1);
|
|
2340
|
+
this.writeByte(17);
|
|
2341
|
+
this.writeByte(0);
|
|
2342
|
+
this.writeByte(2);
|
|
2343
|
+
this.writeByte(17);
|
|
2344
|
+
this.writeByte(1);
|
|
2345
|
+
this.writeByte(3);
|
|
2346
|
+
this.writeByte(17);
|
|
2347
|
+
this.writeByte(1);
|
|
2348
|
+
this.writeWord(65476);
|
|
2349
|
+
this.writeWord(418);
|
|
2350
|
+
this.writeByte(0);
|
|
2351
|
+
for (let i = 0; i < 16; i++)
|
|
2352
|
+
this.writeByte(STD_DC_LUMINANCE_NRCODES[i + 1]);
|
|
2353
|
+
for (let i = 0; i <= 11; i++)
|
|
2354
|
+
this.writeByte(STD_DC_LUMINANCE_VALUES[i]);
|
|
2355
|
+
this.writeByte(16);
|
|
2356
|
+
for (let i = 0; i < 16; i++)
|
|
2357
|
+
this.writeByte(STD_AC_LUMINANCE_NRCODES[i + 1]);
|
|
2358
|
+
for (let i = 0; i <= 161; i++)
|
|
2359
|
+
this.writeByte(STD_AC_LUMINANCE_VALUES[i]);
|
|
2360
|
+
this.writeByte(1);
|
|
2361
|
+
for (let i = 0; i < 16; i++)
|
|
2362
|
+
this.writeByte(STD_DC_CHROMINANCE_NRCODES[i + 1]);
|
|
2363
|
+
for (let i = 0; i <= 11; i++)
|
|
2364
|
+
this.writeByte(STD_DC_CHROMINANCE_VALUES[i]);
|
|
2365
|
+
this.writeByte(17);
|
|
2366
|
+
for (let i = 0; i < 16; i++)
|
|
2367
|
+
this.writeByte(STD_AC_CHROMINANCE_NRCODES[i + 1]);
|
|
2368
|
+
for (let i = 0; i <= 161; i++)
|
|
2369
|
+
this.writeByte(STD_AC_CHROMINANCE_VALUES[i]);
|
|
2370
|
+
this.writeWord(65498);
|
|
2371
|
+
this.writeWord(12);
|
|
2372
|
+
this.writeByte(3);
|
|
2373
|
+
this.writeByte(1);
|
|
2374
|
+
this.writeByte(0);
|
|
2375
|
+
this.writeByte(2);
|
|
2376
|
+
this.writeByte(17);
|
|
2377
|
+
this.writeByte(3);
|
|
2378
|
+
this.writeByte(17);
|
|
2379
|
+
this.writeByte(0);
|
|
2380
|
+
this.writeByte(63);
|
|
2381
|
+
this.writeByte(0);
|
|
2382
|
+
let DCY = 0;
|
|
2383
|
+
let DCU = 0;
|
|
2384
|
+
let DCV = 0;
|
|
2385
|
+
this.bytenew = 0;
|
|
2386
|
+
this.bytepos = 7;
|
|
2387
|
+
const YDU = new Float32Array(64);
|
|
2388
|
+
const UDU = new Float32Array(64);
|
|
2389
|
+
const VDU = new Float32Array(64);
|
|
2390
|
+
const quadWidth = width * 4;
|
|
2391
|
+
const fdtbl_Y = this.fdtbl_Y;
|
|
2392
|
+
const fdtbl_UV = this.fdtbl_UV;
|
|
2393
|
+
const RGB_YUV_TABLE = this.RGB_YUV_TABLE;
|
|
2394
|
+
for (let y = 0; y < height; y += 8) {
|
|
2395
|
+
for (let x = 0; x < width; x += 8) {
|
|
2396
|
+
let start = quadWidth * y + x * 4;
|
|
2397
|
+
for (let pos = 0; pos < 64; pos++) {
|
|
2398
|
+
const row = pos >> 3;
|
|
2399
|
+
const col = (pos & 7) * 4;
|
|
2400
|
+
let p = start + row * quadWidth + col;
|
|
2401
|
+
if (y + row >= height)
|
|
2402
|
+
p -= quadWidth * (y + row - height + 1);
|
|
2403
|
+
if (x + (col >> 2) >= width)
|
|
2404
|
+
p -= 4 * (x + (col >> 2) - width + 1);
|
|
2405
|
+
const r = rgba[p];
|
|
2406
|
+
const g = rgba[p + 1];
|
|
2407
|
+
const b = rgba[p + 2];
|
|
2408
|
+
YDU[pos] = (RGB_YUV_TABLE[r] + RGB_YUV_TABLE[g + 256] + RGB_YUV_TABLE[b + 512] >> 16) - 128;
|
|
2409
|
+
UDU[pos] = (RGB_YUV_TABLE[r + 768] + RGB_YUV_TABLE[g + 1024] + RGB_YUV_TABLE[b + 1280] >> 16) - 128;
|
|
2410
|
+
VDU[pos] = (RGB_YUV_TABLE[r + 1280] + RGB_YUV_TABLE[g + 1536] + RGB_YUV_TABLE[b + 1792] >> 16) - 128;
|
|
2411
|
+
}
|
|
2412
|
+
DCY = this.processDU(YDU, fdtbl_Y, DCY, this.YDC_HT, this.YAC_HT);
|
|
2413
|
+
DCU = this.processDU(UDU, fdtbl_UV, DCU, this.UVDC_HT, this.UVAC_HT);
|
|
2414
|
+
DCV = this.processDU(VDU, fdtbl_UV, DCV, this.UVDC_HT, this.UVAC_HT);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
if (this.bytepos >= 0) {
|
|
2418
|
+
const fillbits = { length: this.bytepos + 1, code: (1 << this.bytepos + 1) - 1 };
|
|
2419
|
+
this.writeBits(fillbits);
|
|
2420
|
+
}
|
|
2421
|
+
this.writeWord(65497);
|
|
2422
|
+
return Buffer.from(this.byteout);
|
|
2423
|
+
}
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
});
|
|
2427
|
+
|
|
1139
2428
|
// ../core/dist/png-encoder.js
|
|
1140
2429
|
import { deflateSync } from "zlib";
|
|
1141
2430
|
function crc32(buf) {
|
|
@@ -1199,12 +2488,18 @@ var init_png_encoder = __esm({
|
|
|
1199
2488
|
import * as fs from "fs";
|
|
1200
2489
|
import * as os from "os";
|
|
1201
2490
|
import * as path from "path";
|
|
1202
|
-
function
|
|
2491
|
+
function encodeImageFromRgbaResponse(response, format, quality) {
|
|
1203
2492
|
if (!response.data || response.width === void 0 || response.height === void 0) {
|
|
1204
2493
|
throw new Error("Render response missing data, width, or height");
|
|
1205
2494
|
}
|
|
1206
2495
|
const rgbaBuffer = Buffer.from(response.data, "base64");
|
|
1207
|
-
|
|
2496
|
+
if (format === "png") {
|
|
2497
|
+
return { buffer: rgbaToPng(rgbaBuffer, response.width, response.height), mimeType: "image/png" };
|
|
2498
|
+
}
|
|
2499
|
+
return {
|
|
2500
|
+
buffer: rgbaToJpeg(rgbaBuffer, response.width, response.height, quality),
|
|
2501
|
+
mimeType: "image/jpeg"
|
|
2502
|
+
};
|
|
1208
2503
|
}
|
|
1209
2504
|
function luaLongQuote(s) {
|
|
1210
2505
|
let level = 0;
|
|
@@ -1215,8 +2510,16 @@ function luaLongQuote(s) {
|
|
|
1215
2510
|
${s}
|
|
1216
2511
|
]${eq}]`;
|
|
1217
2512
|
}
|
|
2513
|
+
function evalCountLines(s) {
|
|
2514
|
+
return s.split("\n").length;
|
|
2515
|
+
}
|
|
1218
2516
|
function buildModuleScriptInvokeWrapper(opts) {
|
|
2517
|
+
const userLines = evalCountLines(opts.userCode);
|
|
1219
2518
|
const wrapped = `return ((function()
|
|
2519
|
+
local __mcp_traceback
|
|
2520
|
+
local __mcp_remap
|
|
2521
|
+
local __mcp_LINE_OFFSET = ${EVAL_WRAPPER_LINE_OFFSET}
|
|
2522
|
+
local __mcp_USER_LINES = ${userLines}
|
|
1220
2523
|
local __mcp_output = {}
|
|
1221
2524
|
local __mcp_real_print = print
|
|
1222
2525
|
local __mcp_real_warn = warn
|
|
@@ -1237,7 +2540,65 @@ function buildModuleScriptInvokeWrapper(opts) {
|
|
|
1237
2540
|
local function __mcp_run()
|
|
1238
2541
|
${opts.userCode}
|
|
1239
2542
|
end
|
|
1240
|
-
|
|
2543
|
+
__mcp_remap = function(s)
|
|
2544
|
+
-- Two chunk-name formats can reference our payload: the
|
|
2545
|
+
-- ModuleScript path "Workspace.__MCPEvalPayload:N" and the
|
|
2546
|
+
-- loadstring chunk "[string \\"return ((function()...\\"]:N" (if
|
|
2547
|
+
-- the IIFE happens to compile via loadstring). Normalize both to
|
|
2548
|
+
-- "user_code:N" with the offset stripped AND clamped to user
|
|
2549
|
+
-- range, otherwise unclosed constructs report nonsense lines deep
|
|
2550
|
+
-- in the wrapper. Strip the "Workspace." parent prefix too so the
|
|
2551
|
+
-- final output reads "user_code:N" not "Workspace.user_code:N".
|
|
2552
|
+
local function __mcp_user_line(payload_n)
|
|
2553
|
+
local user_n = payload_n - __mcp_LINE_OFFSET
|
|
2554
|
+
if user_n < 1 then return "1" end
|
|
2555
|
+
if user_n > __mcp_USER_LINES then return tostring(__mcp_USER_LINES) .. " (at end of input)" end
|
|
2556
|
+
return tostring(user_n)
|
|
2557
|
+
end
|
|
2558
|
+
s = string.gsub(s, "Workspace%.__MCPEvalPayload:(%d+)", function(num)
|
|
2559
|
+
local n = tonumber(num)
|
|
2560
|
+
if n then return "user_code:" .. __mcp_user_line(n) end
|
|
2561
|
+
return "user_code:" .. num
|
|
2562
|
+
end)
|
|
2563
|
+
s = string.gsub(s, "__MCPEvalPayload:(%d+)", function(num)
|
|
2564
|
+
local n = tonumber(num)
|
|
2565
|
+
if n then return "user_code:" .. __mcp_user_line(n) end
|
|
2566
|
+
return "user_code:" .. num
|
|
2567
|
+
end)
|
|
2568
|
+
s = string.gsub(s, '%[string "[^"]+"%]:(%d+)', function(num)
|
|
2569
|
+
local n = tonumber(num)
|
|
2570
|
+
if n then return "user_code:" .. __mcp_user_line(n) end
|
|
2571
|
+
return "user_code:" .. num
|
|
2572
|
+
end)
|
|
2573
|
+
return s
|
|
2574
|
+
end
|
|
2575
|
+
__mcp_traceback = function(err)
|
|
2576
|
+
local raw = debug.traceback(tostring(err), 2)
|
|
2577
|
+
local kept = {}
|
|
2578
|
+
for line in string.gmatch(raw, "[^\\n]+") do
|
|
2579
|
+
local num_str = string.match(line, "__MCPEvalPayload:(%d+)")
|
|
2580
|
+
or string.match(line, '%[string "[^"]+"%]:(%d+)')
|
|
2581
|
+
local n = num_str and tonumber(num_str)
|
|
2582
|
+
-- Strip "in function '__mcp_run'" annotation BEFORE filtering:
|
|
2583
|
+
-- user-code frames all carry that suffix (their source is
|
|
2584
|
+
-- hosted inside __mcp_run), so a naive "__mcp_" filter would
|
|
2585
|
+
-- drop every user frame and leave only the error header.
|
|
2586
|
+
line = (string.gsub(line, " in function '__mcp_run'", ""))
|
|
2587
|
+
local skip = string.find(line, "MCPPlugin", 1, true)
|
|
2588
|
+
or string.find(line, "__mcp_", 1, true)
|
|
2589
|
+
or string.find(line, "in function 'xpcall'", 1, true)
|
|
2590
|
+
-- Drop wrapper preamble/postamble frames whose line falls
|
|
2591
|
+
-- outside the user-code range \u2014 those are wrapper internals.
|
|
2592
|
+
if n and (n <= __mcp_LINE_OFFSET or n > __mcp_LINE_OFFSET + __mcp_USER_LINES) then
|
|
2593
|
+
skip = true
|
|
2594
|
+
end
|
|
2595
|
+
if not skip then
|
|
2596
|
+
table.insert(kept, __mcp_remap(line))
|
|
2597
|
+
end
|
|
2598
|
+
end
|
|
2599
|
+
return table.concat(kept, "\\n")
|
|
2600
|
+
end
|
|
2601
|
+
local ok, errOrValue = xpcall(__mcp_run, __mcp_traceback)
|
|
1241
2602
|
return { ok = ok, value = errOrValue, output = __mcp_output }
|
|
1242
2603
|
end)())`;
|
|
1243
2604
|
return `
|
|
@@ -1249,6 +2610,46 @@ if not bf then
|
|
|
1249
2610
|
error = ${luaLongQuote(opts.missingError)},
|
|
1250
2611
|
})
|
|
1251
2612
|
end
|
|
2613
|
+
-- Outer-scope mirror of the in-IIFE __mcp_remap. Applied to parser errors
|
|
2614
|
+
-- we pull out of LogService (those never pass through the IIFE) and to
|
|
2615
|
+
-- the canned engine error string. Same offset as the IIFE's
|
|
2616
|
+
-- __mcp_LINE_OFFSET; covers both chunk-name formats.
|
|
2617
|
+
local __mcp_USER_LINES_OUTER = ${userLines}
|
|
2618
|
+
local function __mcp_outer_user_line(payload_n)
|
|
2619
|
+
local user_n = payload_n - ${EVAL_WRAPPER_LINE_OFFSET}
|
|
2620
|
+
if user_n < 1 then return "1" end
|
|
2621
|
+
if user_n > __mcp_USER_LINES_OUTER then return tostring(__mcp_USER_LINES_OUTER) .. " (at end of input)" end
|
|
2622
|
+
return tostring(user_n)
|
|
2623
|
+
end
|
|
2624
|
+
local function __mcp_outer_remap(s)
|
|
2625
|
+
s = string.gsub(s, "Workspace%.__MCPEvalPayload:(%d+)", function(num)
|
|
2626
|
+
local n = tonumber(num)
|
|
2627
|
+
if n then return "user_code:" .. __mcp_outer_user_line(n) end
|
|
2628
|
+
return "user_code:" .. num
|
|
2629
|
+
end)
|
|
2630
|
+
s = string.gsub(s, "__MCPEvalPayload:(%d+)", function(num)
|
|
2631
|
+
local n = tonumber(num)
|
|
2632
|
+
if n then return "user_code:" .. __mcp_outer_user_line(n) end
|
|
2633
|
+
return "user_code:" .. num
|
|
2634
|
+
end)
|
|
2635
|
+
s = string.gsub(s, '%[string "[^"]+"%]:(%d+)', function(num)
|
|
2636
|
+
local n = tonumber(num)
|
|
2637
|
+
if n then return "user_code:" .. __mcp_outer_user_line(n) end
|
|
2638
|
+
return "user_code:" .. num
|
|
2639
|
+
end)
|
|
2640
|
+
return s
|
|
2641
|
+
end
|
|
2642
|
+
-- JSON-encode tables; otherwise tostring. Cycles or non-serializable
|
|
2643
|
+
-- values fall back to tostring instead of erroring. This is what makes
|
|
2644
|
+
-- eval_server_runtime / eval_client_runtime return structured table data
|
|
2645
|
+
-- (matching execute_luau) instead of "table: 0xaddr".
|
|
2646
|
+
local function __mcp_format(v)
|
|
2647
|
+
if typeof(v) == "table" then
|
|
2648
|
+
local ok, encoded = pcall(function() return HttpService:JSONEncode(v) end)
|
|
2649
|
+
if ok then return encoded end
|
|
2650
|
+
end
|
|
2651
|
+
return tostring(v)
|
|
2652
|
+
end
|
|
1252
2653
|
local USER_CODE = ${luaLongQuote(wrapped)}
|
|
1253
2654
|
local m = Instance.new("ModuleScript")
|
|
1254
2655
|
m.Name = "__MCPEvalPayload"
|
|
@@ -1282,7 +2683,7 @@ if not bridgeOk then
|
|
|
1282
2683
|
end
|
|
1283
2684
|
end
|
|
1284
2685
|
end
|
|
1285
|
-
return HttpService:JSONEncode({ bridge = "ok", ok = false, error = errMsg })
|
|
2686
|
+
return HttpService:JSONEncode({ bridge = "ok", ok = false, error = __mcp_outer_remap(errMsg) })
|
|
1286
2687
|
end
|
|
1287
2688
|
-- inner is the {ok, value, output} table from our IIFE. Defensive: if it's
|
|
1288
2689
|
-- somehow not a table (caller bypassed the wrapper), fall back to old shape.
|
|
@@ -1290,13 +2691,13 @@ if typeof(inner) ~= "table" then
|
|
|
1290
2691
|
return HttpService:JSONEncode({
|
|
1291
2692
|
bridge = "ok",
|
|
1292
2693
|
ok = true,
|
|
1293
|
-
result = if inner == nil then nil else
|
|
2694
|
+
result = if inner == nil then nil else __mcp_format(inner),
|
|
1294
2695
|
})
|
|
1295
2696
|
end
|
|
1296
2697
|
return HttpService:JSONEncode({
|
|
1297
2698
|
bridge = "ok",
|
|
1298
2699
|
ok = inner.ok == true,
|
|
1299
|
-
result = if inner.ok and inner.value ~= nil then
|
|
2700
|
+
result = if inner.ok and inner.value ~= nil then __mcp_format(inner.value) else nil,
|
|
1300
2701
|
error = if not inner.ok then tostring(inner.value) else nil,
|
|
1301
2702
|
output = inner.output or {},
|
|
1302
2703
|
})
|
|
@@ -1313,17 +2714,20 @@ function parseBridgeResponse(response) {
|
|
|
1313
2714
|
}
|
|
1314
2715
|
return JSON.stringify(response);
|
|
1315
2716
|
}
|
|
1316
|
-
var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, RobloxStudioTools;
|
|
2717
|
+
var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, EVAL_WRAPPER_LINE_OFFSET, RobloxStudioTools;
|
|
1317
2718
|
var init_tools = __esm({
|
|
1318
2719
|
"../core/dist/tools/index.js"() {
|
|
1319
2720
|
"use strict";
|
|
1320
2721
|
init_studio_client();
|
|
2722
|
+
init_bridge_service();
|
|
1321
2723
|
init_build_executor();
|
|
1322
2724
|
init_opencloud_client();
|
|
1323
2725
|
init_roblox_cookie_client();
|
|
2726
|
+
init_jpeg_encoder();
|
|
1324
2727
|
init_png_encoder();
|
|
1325
2728
|
SERVER_LOCAL_NAME = "__MCP_ServerEvalLocal";
|
|
1326
2729
|
CLIENT_LOCAL_NAME = "__MCP_ClientEvalBridge";
|
|
2730
|
+
EVAL_WRAPPER_LINE_OFFSET = 23;
|
|
1327
2731
|
RobloxStudioTools = class _RobloxStudioTools {
|
|
1328
2732
|
client;
|
|
1329
2733
|
bridge;
|
|
@@ -1335,8 +2739,43 @@ var init_tools = __esm({
|
|
|
1335
2739
|
this.openCloudClient = new OpenCloudClient();
|
|
1336
2740
|
this.cookieClient = new RobloxCookieClient();
|
|
1337
2741
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
2742
|
+
// Resolve (instance_id, target-role) → concrete (instanceId, role) and
|
|
2743
|
+
// dispatch a single request. Throws RoutingFailure if the resolution is
|
|
2744
|
+
// ambiguous, missing, or asks for fanout on a non-fanout-capable tool —
|
|
2745
|
+
// the MCP transport layer surfaces it as a structured error result so
|
|
2746
|
+
// the LLM can recover via the embedded data.instances list.
|
|
2747
|
+
async _callSingle(endpoint, data, target, instance_id) {
|
|
2748
|
+
const r = this.bridge.resolveTarget({ instance_id, target });
|
|
2749
|
+
if (!r.ok)
|
|
2750
|
+
throw new RoutingFailure(r.error);
|
|
2751
|
+
if (r.mode !== "single") {
|
|
2752
|
+
throw new RoutingFailure({
|
|
2753
|
+
code: "target_role_not_present_on_instance",
|
|
2754
|
+
message: "This tool does not support target=all. Pick a specific role or omit target.",
|
|
2755
|
+
data: {
|
|
2756
|
+
instances: this.bridge.getPublicInstances(),
|
|
2757
|
+
count: this.bridge.getInstances().length
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
}
|
|
2761
|
+
return this.client.request(endpoint, data, r.targetInstanceId, r.targetRole);
|
|
2762
|
+
}
|
|
2763
|
+
// Resolves which connected place a tool should target and whether a playtest
|
|
2764
|
+
// CLIENT peer is present on it. Used by capture/input to auto-route to the
|
|
2765
|
+
// running client (where the live viewport + input pipeline are) without the
|
|
2766
|
+
// caller having to pass target. Throws RoutingFailure with the standard
|
|
2767
|
+
// instance list if the place is ambiguous (multiple connected, no instance_id).
|
|
2768
|
+
_resolveRuntime(instance_id) {
|
|
2769
|
+
const r = this.bridge.resolveTarget({ instance_id, target: void 0 });
|
|
2770
|
+
if (!r.ok)
|
|
2771
|
+
throw new RoutingFailure(r.error);
|
|
2772
|
+
const resolvedId = r.targetInstanceId;
|
|
2773
|
+
const roles = this.bridge.getInstances().filter((i) => i.instanceId === resolvedId).map((i) => i.role);
|
|
2774
|
+
const clientRoles = roles.filter((role) => role.startsWith("client")).sort();
|
|
2775
|
+
return { instanceId: resolvedId, clientRole: clientRoles[0] };
|
|
2776
|
+
}
|
|
2777
|
+
async getFileTree(path2 = "", instance_id) {
|
|
2778
|
+
const response = await this._callSingle("/api/file-tree", { path: path2 }, void 0, instance_id);
|
|
1340
2779
|
return {
|
|
1341
2780
|
content: [
|
|
1342
2781
|
{
|
|
@@ -1346,8 +2785,8 @@ var init_tools = __esm({
|
|
|
1346
2785
|
]
|
|
1347
2786
|
};
|
|
1348
2787
|
}
|
|
1349
|
-
async searchFiles(query, searchType = "name") {
|
|
1350
|
-
const response = await this.
|
|
2788
|
+
async searchFiles(query, searchType = "name", instance_id) {
|
|
2789
|
+
const response = await this._callSingle("/api/search-files", { query, searchType }, void 0, instance_id);
|
|
1351
2790
|
return {
|
|
1352
2791
|
content: [
|
|
1353
2792
|
{
|
|
@@ -1357,8 +2796,8 @@ var init_tools = __esm({
|
|
|
1357
2796
|
]
|
|
1358
2797
|
};
|
|
1359
2798
|
}
|
|
1360
|
-
async getPlaceInfo() {
|
|
1361
|
-
const response = await this.
|
|
2799
|
+
async getPlaceInfo(instance_id) {
|
|
2800
|
+
const response = await this._callSingle("/api/place-info", {}, void 0, instance_id);
|
|
1362
2801
|
return {
|
|
1363
2802
|
content: [
|
|
1364
2803
|
{
|
|
@@ -1368,8 +2807,8 @@ var init_tools = __esm({
|
|
|
1368
2807
|
]
|
|
1369
2808
|
};
|
|
1370
2809
|
}
|
|
1371
|
-
async getServices(serviceName) {
|
|
1372
|
-
const response = await this.
|
|
2810
|
+
async getServices(serviceName, instance_id) {
|
|
2811
|
+
const response = await this._callSingle("/api/services", { serviceName }, void 0, instance_id);
|
|
1373
2812
|
return {
|
|
1374
2813
|
content: [
|
|
1375
2814
|
{
|
|
@@ -1379,12 +2818,12 @@ var init_tools = __esm({
|
|
|
1379
2818
|
]
|
|
1380
2819
|
};
|
|
1381
2820
|
}
|
|
1382
|
-
async searchObjects(query, searchType = "name", propertyName) {
|
|
1383
|
-
const response = await this.
|
|
2821
|
+
async searchObjects(query, searchType = "name", propertyName, instance_id) {
|
|
2822
|
+
const response = await this._callSingle("/api/search-objects", {
|
|
1384
2823
|
query,
|
|
1385
2824
|
searchType,
|
|
1386
2825
|
propertyName
|
|
1387
|
-
});
|
|
2826
|
+
}, void 0, instance_id);
|
|
1388
2827
|
return {
|
|
1389
2828
|
content: [
|
|
1390
2829
|
{
|
|
@@ -1394,11 +2833,11 @@ var init_tools = __esm({
|
|
|
1394
2833
|
]
|
|
1395
2834
|
};
|
|
1396
2835
|
}
|
|
1397
|
-
async getInstanceProperties(instancePath, excludeSource) {
|
|
2836
|
+
async getInstanceProperties(instancePath, excludeSource, instance_id) {
|
|
1398
2837
|
if (!instancePath) {
|
|
1399
2838
|
throw new Error("Instance path is required for get_instance_properties");
|
|
1400
2839
|
}
|
|
1401
|
-
const response = await this.
|
|
2840
|
+
const response = await this._callSingle("/api/instance-properties", { instancePath, excludeSource }, void 0, instance_id);
|
|
1402
2841
|
return {
|
|
1403
2842
|
content: [
|
|
1404
2843
|
{
|
|
@@ -1408,11 +2847,11 @@ var init_tools = __esm({
|
|
|
1408
2847
|
]
|
|
1409
2848
|
};
|
|
1410
2849
|
}
|
|
1411
|
-
async getInstanceChildren(instancePath) {
|
|
2850
|
+
async getInstanceChildren(instancePath, instance_id) {
|
|
1412
2851
|
if (!instancePath) {
|
|
1413
2852
|
throw new Error("Instance path is required for get_instance_children");
|
|
1414
2853
|
}
|
|
1415
|
-
const response = await this.
|
|
2854
|
+
const response = await this._callSingle("/api/instance-children", { instancePath }, void 0, instance_id);
|
|
1416
2855
|
return {
|
|
1417
2856
|
content: [
|
|
1418
2857
|
{
|
|
@@ -1422,14 +2861,14 @@ var init_tools = __esm({
|
|
|
1422
2861
|
]
|
|
1423
2862
|
};
|
|
1424
2863
|
}
|
|
1425
|
-
async searchByProperty(propertyName, propertyValue) {
|
|
2864
|
+
async searchByProperty(propertyName, propertyValue, instance_id) {
|
|
1426
2865
|
if (!propertyName || !propertyValue) {
|
|
1427
2866
|
throw new Error("Property name and value are required for search_by_property");
|
|
1428
2867
|
}
|
|
1429
|
-
const response = await this.
|
|
2868
|
+
const response = await this._callSingle("/api/search-by-property", {
|
|
1430
2869
|
propertyName,
|
|
1431
2870
|
propertyValue
|
|
1432
|
-
});
|
|
2871
|
+
}, void 0, instance_id);
|
|
1433
2872
|
return {
|
|
1434
2873
|
content: [
|
|
1435
2874
|
{
|
|
@@ -1439,11 +2878,11 @@ var init_tools = __esm({
|
|
|
1439
2878
|
]
|
|
1440
2879
|
};
|
|
1441
2880
|
}
|
|
1442
|
-
async getClassInfo(className) {
|
|
2881
|
+
async getClassInfo(className, instance_id) {
|
|
1443
2882
|
if (!className) {
|
|
1444
2883
|
throw new Error("Class name is required for get_class_info");
|
|
1445
2884
|
}
|
|
1446
|
-
const response = await this.
|
|
2885
|
+
const response = await this._callSingle("/api/class-info", { className }, void 0, instance_id);
|
|
1447
2886
|
return {
|
|
1448
2887
|
content: [
|
|
1449
2888
|
{
|
|
@@ -1453,12 +2892,12 @@ var init_tools = __esm({
|
|
|
1453
2892
|
]
|
|
1454
2893
|
};
|
|
1455
2894
|
}
|
|
1456
|
-
async getProjectStructure(path2, maxDepth, scriptsOnly) {
|
|
1457
|
-
const response = await this.
|
|
2895
|
+
async getProjectStructure(path2, maxDepth, scriptsOnly, instance_id) {
|
|
2896
|
+
const response = await this._callSingle("/api/project-structure", {
|
|
1458
2897
|
path: path2,
|
|
1459
2898
|
maxDepth,
|
|
1460
2899
|
scriptsOnly
|
|
1461
|
-
});
|
|
2900
|
+
}, void 0, instance_id);
|
|
1462
2901
|
return {
|
|
1463
2902
|
content: [
|
|
1464
2903
|
{
|
|
@@ -1468,15 +2907,15 @@ var init_tools = __esm({
|
|
|
1468
2907
|
]
|
|
1469
2908
|
};
|
|
1470
2909
|
}
|
|
1471
|
-
async setProperty(instancePath, propertyName, propertyValue) {
|
|
2910
|
+
async setProperty(instancePath, propertyName, propertyValue, instance_id) {
|
|
1472
2911
|
if (!instancePath || !propertyName) {
|
|
1473
2912
|
throw new Error("Instance path and property name are required for set_property");
|
|
1474
2913
|
}
|
|
1475
|
-
const response = await this.
|
|
2914
|
+
const response = await this._callSingle("/api/set-property", {
|
|
1476
2915
|
instancePath,
|
|
1477
2916
|
propertyName,
|
|
1478
2917
|
propertyValue
|
|
1479
|
-
});
|
|
2918
|
+
}, void 0, instance_id);
|
|
1480
2919
|
return {
|
|
1481
2920
|
content: [
|
|
1482
2921
|
{
|
|
@@ -1486,22 +2925,22 @@ var init_tools = __esm({
|
|
|
1486
2925
|
]
|
|
1487
2926
|
};
|
|
1488
2927
|
}
|
|
1489
|
-
async setProperties(instancePath, properties) {
|
|
2928
|
+
async setProperties(instancePath, properties, instance_id) {
|
|
1490
2929
|
if (!instancePath || !properties) {
|
|
1491
2930
|
throw new Error("instancePath and properties are required for set_properties");
|
|
1492
2931
|
}
|
|
1493
|
-
const response = await this.
|
|
2932
|
+
const response = await this._callSingle("/api/set-properties", { instancePath, properties }, void 0, instance_id);
|
|
1494
2933
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
1495
2934
|
}
|
|
1496
|
-
async massSetProperty(paths, propertyName, propertyValue) {
|
|
2935
|
+
async massSetProperty(paths, propertyName, propertyValue, instance_id) {
|
|
1497
2936
|
if (!paths || paths.length === 0 || !propertyName) {
|
|
1498
2937
|
throw new Error("Paths array and property name are required for mass_set_property");
|
|
1499
2938
|
}
|
|
1500
|
-
const response = await this.
|
|
2939
|
+
const response = await this._callSingle("/api/mass-set-property", {
|
|
1501
2940
|
paths,
|
|
1502
2941
|
propertyName,
|
|
1503
2942
|
propertyValue
|
|
1504
|
-
});
|
|
2943
|
+
}, void 0, instance_id);
|
|
1505
2944
|
return {
|
|
1506
2945
|
content: [
|
|
1507
2946
|
{
|
|
@@ -1511,14 +2950,14 @@ var init_tools = __esm({
|
|
|
1511
2950
|
]
|
|
1512
2951
|
};
|
|
1513
2952
|
}
|
|
1514
|
-
async massGetProperty(paths, propertyName) {
|
|
2953
|
+
async massGetProperty(paths, propertyName, instance_id) {
|
|
1515
2954
|
if (!paths || paths.length === 0 || !propertyName) {
|
|
1516
2955
|
throw new Error("Paths array and property name are required for mass_get_property");
|
|
1517
2956
|
}
|
|
1518
|
-
const response = await this.
|
|
2957
|
+
const response = await this._callSingle("/api/mass-get-property", {
|
|
1519
2958
|
paths,
|
|
1520
2959
|
propertyName
|
|
1521
|
-
});
|
|
2960
|
+
}, void 0, instance_id);
|
|
1522
2961
|
return {
|
|
1523
2962
|
content: [
|
|
1524
2963
|
{
|
|
@@ -1528,16 +2967,16 @@ var init_tools = __esm({
|
|
|
1528
2967
|
]
|
|
1529
2968
|
};
|
|
1530
2969
|
}
|
|
1531
|
-
async createObject(className, parent, name, properties) {
|
|
2970
|
+
async createObject(className, parent, name, properties, instance_id) {
|
|
1532
2971
|
if (!className || !parent) {
|
|
1533
2972
|
throw new Error("Class name and parent are required for create_object");
|
|
1534
2973
|
}
|
|
1535
|
-
const response = await this.
|
|
2974
|
+
const response = await this._callSingle("/api/create-object", {
|
|
1536
2975
|
className,
|
|
1537
2976
|
parent,
|
|
1538
2977
|
name,
|
|
1539
2978
|
properties
|
|
1540
|
-
});
|
|
2979
|
+
}, void 0, instance_id);
|
|
1541
2980
|
return {
|
|
1542
2981
|
content: [
|
|
1543
2982
|
{
|
|
@@ -1547,11 +2986,11 @@ var init_tools = __esm({
|
|
|
1547
2986
|
]
|
|
1548
2987
|
};
|
|
1549
2988
|
}
|
|
1550
|
-
async massCreateObjects(objects) {
|
|
2989
|
+
async massCreateObjects(objects, instance_id) {
|
|
1551
2990
|
if (!objects || objects.length === 0) {
|
|
1552
2991
|
throw new Error("Objects array is required for mass_create_objects");
|
|
1553
2992
|
}
|
|
1554
|
-
const response = await this.
|
|
2993
|
+
const response = await this._callSingle("/api/mass-create-objects", { objects }, void 0, instance_id);
|
|
1555
2994
|
return {
|
|
1556
2995
|
content: [
|
|
1557
2996
|
{
|
|
@@ -1561,11 +3000,11 @@ var init_tools = __esm({
|
|
|
1561
3000
|
]
|
|
1562
3001
|
};
|
|
1563
3002
|
}
|
|
1564
|
-
async deleteObject(instancePath) {
|
|
3003
|
+
async deleteObject(instancePath, instance_id) {
|
|
1565
3004
|
if (!instancePath) {
|
|
1566
3005
|
throw new Error("Instance path is required for delete_object");
|
|
1567
3006
|
}
|
|
1568
|
-
const response = await this.
|
|
3007
|
+
const response = await this._callSingle("/api/delete-object", { instancePath }, void 0, instance_id);
|
|
1569
3008
|
return {
|
|
1570
3009
|
content: [
|
|
1571
3010
|
{
|
|
@@ -1575,15 +3014,15 @@ var init_tools = __esm({
|
|
|
1575
3014
|
]
|
|
1576
3015
|
};
|
|
1577
3016
|
}
|
|
1578
|
-
async smartDuplicate(instancePath, count, options) {
|
|
3017
|
+
async smartDuplicate(instancePath, count, options, instance_id) {
|
|
1579
3018
|
if (!instancePath || count < 1) {
|
|
1580
3019
|
throw new Error("Instance path and count > 0 are required for smart_duplicate");
|
|
1581
3020
|
}
|
|
1582
|
-
const response = await this.
|
|
3021
|
+
const response = await this._callSingle("/api/smart-duplicate", {
|
|
1583
3022
|
instancePath,
|
|
1584
3023
|
count,
|
|
1585
3024
|
options
|
|
1586
|
-
});
|
|
3025
|
+
}, void 0, instance_id);
|
|
1587
3026
|
return {
|
|
1588
3027
|
content: [
|
|
1589
3028
|
{
|
|
@@ -1593,11 +3032,11 @@ var init_tools = __esm({
|
|
|
1593
3032
|
]
|
|
1594
3033
|
};
|
|
1595
3034
|
}
|
|
1596
|
-
async massDuplicate(duplications) {
|
|
3035
|
+
async massDuplicate(duplications, instance_id) {
|
|
1597
3036
|
if (!duplications || duplications.length === 0) {
|
|
1598
3037
|
throw new Error("Duplications array is required for mass_duplicate");
|
|
1599
3038
|
}
|
|
1600
|
-
const response = await this.
|
|
3039
|
+
const response = await this._callSingle("/api/mass-duplicate", { duplications }, void 0, instance_id);
|
|
1601
3040
|
return {
|
|
1602
3041
|
content: [
|
|
1603
3042
|
{
|
|
@@ -1607,11 +3046,11 @@ var init_tools = __esm({
|
|
|
1607
3046
|
]
|
|
1608
3047
|
};
|
|
1609
3048
|
}
|
|
1610
|
-
async getScriptSource(instancePath, startLine, endLine) {
|
|
3049
|
+
async getScriptSource(instancePath, startLine, endLine, instance_id) {
|
|
1611
3050
|
if (!instancePath) {
|
|
1612
3051
|
throw new Error("Instance path is required for get_script_source");
|
|
1613
3052
|
}
|
|
1614
|
-
const response = await this.
|
|
3053
|
+
const response = await this._callSingle("/api/get-script-source", { instancePath, startLine, endLine }, void 0, instance_id);
|
|
1615
3054
|
if (response.error) {
|
|
1616
3055
|
return { content: [{ type: "text", text: `Error: ${response.error}` }] };
|
|
1617
3056
|
}
|
|
@@ -1658,11 +3097,11 @@ ${code}`
|
|
|
1658
3097
|
}]
|
|
1659
3098
|
};
|
|
1660
3099
|
}
|
|
1661
|
-
async setScriptSource(instancePath, source) {
|
|
3100
|
+
async setScriptSource(instancePath, source, instance_id) {
|
|
1662
3101
|
if (!instancePath || typeof source !== "string") {
|
|
1663
3102
|
throw new Error("Instance path and source code string are required for set_script_source");
|
|
1664
3103
|
}
|
|
1665
|
-
const response = await this.
|
|
3104
|
+
const response = await this._callSingle("/api/set-script-source", { instancePath, source }, void 0, instance_id);
|
|
1666
3105
|
return {
|
|
1667
3106
|
content: [
|
|
1668
3107
|
{
|
|
@@ -1672,14 +3111,14 @@ ${code}`
|
|
|
1672
3111
|
]
|
|
1673
3112
|
};
|
|
1674
3113
|
}
|
|
1675
|
-
async editScriptLines(instancePath, oldString, newString, startLine) {
|
|
3114
|
+
async editScriptLines(instancePath, oldString, newString, startLine, instance_id) {
|
|
1676
3115
|
if (!instancePath || typeof oldString !== "string" || typeof newString !== "string") {
|
|
1677
3116
|
throw new Error("Instance path, old_string, and new_string are required for edit_script_lines");
|
|
1678
3117
|
}
|
|
1679
3118
|
const payload = { instancePath, old_string: oldString, new_string: newString };
|
|
1680
3119
|
if (startLine !== void 0)
|
|
1681
3120
|
payload.startLine = startLine;
|
|
1682
|
-
const response = await this.
|
|
3121
|
+
const response = await this._callSingle("/api/edit-script-lines", payload, void 0, instance_id);
|
|
1683
3122
|
return {
|
|
1684
3123
|
content: [
|
|
1685
3124
|
{
|
|
@@ -1689,11 +3128,11 @@ ${code}`
|
|
|
1689
3128
|
]
|
|
1690
3129
|
};
|
|
1691
3130
|
}
|
|
1692
|
-
async insertScriptLines(instancePath, afterLine, newContent) {
|
|
3131
|
+
async insertScriptLines(instancePath, afterLine, newContent, instance_id) {
|
|
1693
3132
|
if (!instancePath || typeof newContent !== "string") {
|
|
1694
3133
|
throw new Error("Instance path and newContent are required for insert_script_lines");
|
|
1695
3134
|
}
|
|
1696
|
-
const response = await this.
|
|
3135
|
+
const response = await this._callSingle("/api/insert-script-lines", { instancePath, afterLine: afterLine || 0, newContent }, void 0, instance_id);
|
|
1697
3136
|
return {
|
|
1698
3137
|
content: [
|
|
1699
3138
|
{
|
|
@@ -1703,11 +3142,11 @@ ${code}`
|
|
|
1703
3142
|
]
|
|
1704
3143
|
};
|
|
1705
3144
|
}
|
|
1706
|
-
async deleteScriptLines(instancePath, startLine, endLine) {
|
|
3145
|
+
async deleteScriptLines(instancePath, startLine, endLine, instance_id) {
|
|
1707
3146
|
if (!instancePath || !startLine || !endLine) {
|
|
1708
3147
|
throw new Error("Instance path, startLine, and endLine are required for delete_script_lines");
|
|
1709
3148
|
}
|
|
1710
|
-
const response = await this.
|
|
3149
|
+
const response = await this._callSingle("/api/delete-script-lines", { instancePath, startLine, endLine }, void 0, instance_id);
|
|
1711
3150
|
return {
|
|
1712
3151
|
content: [
|
|
1713
3152
|
{
|
|
@@ -1717,14 +3156,14 @@ ${code}`
|
|
|
1717
3156
|
]
|
|
1718
3157
|
};
|
|
1719
3158
|
}
|
|
1720
|
-
async grepScripts(pattern, options) {
|
|
3159
|
+
async grepScripts(pattern, options, instance_id) {
|
|
1721
3160
|
if (!pattern) {
|
|
1722
3161
|
throw new Error("Pattern is required for grep_scripts");
|
|
1723
3162
|
}
|
|
1724
|
-
const response = await this.
|
|
3163
|
+
const response = await this._callSingle("/api/grep-scripts", {
|
|
1725
3164
|
pattern,
|
|
1726
3165
|
...options
|
|
1727
|
-
});
|
|
3166
|
+
}, void 0, instance_id);
|
|
1728
3167
|
return {
|
|
1729
3168
|
content: [
|
|
1730
3169
|
{
|
|
@@ -1734,11 +3173,11 @@ ${code}`
|
|
|
1734
3173
|
]
|
|
1735
3174
|
};
|
|
1736
3175
|
}
|
|
1737
|
-
async setAttribute(instancePath, attributeName, attributeValue, valueType) {
|
|
3176
|
+
async setAttribute(instancePath, attributeName, attributeValue, valueType, instance_id) {
|
|
1738
3177
|
if (!instancePath || !attributeName) {
|
|
1739
3178
|
throw new Error("Instance path and attribute name are required for set_attribute");
|
|
1740
3179
|
}
|
|
1741
|
-
const response = await this.
|
|
3180
|
+
const response = await this._callSingle("/api/set-attribute", { instancePath, attributeName, attributeValue, valueType }, void 0, instance_id);
|
|
1742
3181
|
return {
|
|
1743
3182
|
content: [
|
|
1744
3183
|
{
|
|
@@ -1748,11 +3187,11 @@ ${code}`
|
|
|
1748
3187
|
]
|
|
1749
3188
|
};
|
|
1750
3189
|
}
|
|
1751
|
-
async getAttributes(instancePath) {
|
|
3190
|
+
async getAttributes(instancePath, instance_id) {
|
|
1752
3191
|
if (!instancePath) {
|
|
1753
3192
|
throw new Error("Instance path is required for get_attributes");
|
|
1754
3193
|
}
|
|
1755
|
-
const response = await this.
|
|
3194
|
+
const response = await this._callSingle("/api/get-attributes", { instancePath }, void 0, instance_id);
|
|
1756
3195
|
return {
|
|
1757
3196
|
content: [
|
|
1758
3197
|
{
|
|
@@ -1762,11 +3201,11 @@ ${code}`
|
|
|
1762
3201
|
]
|
|
1763
3202
|
};
|
|
1764
3203
|
}
|
|
1765
|
-
async deleteAttribute(instancePath, attributeName) {
|
|
3204
|
+
async deleteAttribute(instancePath, attributeName, instance_id) {
|
|
1766
3205
|
if (!instancePath || !attributeName) {
|
|
1767
3206
|
throw new Error("Instance path and attribute name are required for delete_attribute");
|
|
1768
3207
|
}
|
|
1769
|
-
const response = await this.
|
|
3208
|
+
const response = await this._callSingle("/api/delete-attribute", { instancePath, attributeName }, void 0, instance_id);
|
|
1770
3209
|
return {
|
|
1771
3210
|
content: [
|
|
1772
3211
|
{
|
|
@@ -1776,11 +3215,11 @@ ${code}`
|
|
|
1776
3215
|
]
|
|
1777
3216
|
};
|
|
1778
3217
|
}
|
|
1779
|
-
async getTags(instancePath) {
|
|
3218
|
+
async getTags(instancePath, instance_id) {
|
|
1780
3219
|
if (!instancePath) {
|
|
1781
3220
|
throw new Error("Instance path is required for get_tags");
|
|
1782
3221
|
}
|
|
1783
|
-
const response = await this.
|
|
3222
|
+
const response = await this._callSingle("/api/get-tags", { instancePath }, void 0, instance_id);
|
|
1784
3223
|
return {
|
|
1785
3224
|
content: [
|
|
1786
3225
|
{
|
|
@@ -1790,11 +3229,11 @@ ${code}`
|
|
|
1790
3229
|
]
|
|
1791
3230
|
};
|
|
1792
3231
|
}
|
|
1793
|
-
async addTag(instancePath, tagName) {
|
|
3232
|
+
async addTag(instancePath, tagName, instance_id) {
|
|
1794
3233
|
if (!instancePath || !tagName) {
|
|
1795
3234
|
throw new Error("Instance path and tag name are required for add_tag");
|
|
1796
3235
|
}
|
|
1797
|
-
const response = await this.
|
|
3236
|
+
const response = await this._callSingle("/api/add-tag", { instancePath, tagName }, void 0, instance_id);
|
|
1798
3237
|
return {
|
|
1799
3238
|
content: [
|
|
1800
3239
|
{
|
|
@@ -1804,11 +3243,11 @@ ${code}`
|
|
|
1804
3243
|
]
|
|
1805
3244
|
};
|
|
1806
3245
|
}
|
|
1807
|
-
async removeTag(instancePath, tagName) {
|
|
3246
|
+
async removeTag(instancePath, tagName, instance_id) {
|
|
1808
3247
|
if (!instancePath || !tagName) {
|
|
1809
3248
|
throw new Error("Instance path and tag name are required for remove_tag");
|
|
1810
3249
|
}
|
|
1811
|
-
const response = await this.
|
|
3250
|
+
const response = await this._callSingle("/api/remove-tag", { instancePath, tagName }, void 0, instance_id);
|
|
1812
3251
|
return {
|
|
1813
3252
|
content: [
|
|
1814
3253
|
{
|
|
@@ -1818,11 +3257,11 @@ ${code}`
|
|
|
1818
3257
|
]
|
|
1819
3258
|
};
|
|
1820
3259
|
}
|
|
1821
|
-
async getTagged(tagName) {
|
|
3260
|
+
async getTagged(tagName, instance_id) {
|
|
1822
3261
|
if (!tagName) {
|
|
1823
3262
|
throw new Error("Tag name is required for get_tagged");
|
|
1824
3263
|
}
|
|
1825
|
-
const response = await this.
|
|
3264
|
+
const response = await this._callSingle("/api/get-tagged", { tagName }, void 0, instance_id);
|
|
1826
3265
|
return {
|
|
1827
3266
|
content: [
|
|
1828
3267
|
{
|
|
@@ -1832,8 +3271,8 @@ ${code}`
|
|
|
1832
3271
|
]
|
|
1833
3272
|
};
|
|
1834
3273
|
}
|
|
1835
|
-
async getSelection() {
|
|
1836
|
-
const response = await this.
|
|
3274
|
+
async getSelection(instance_id) {
|
|
3275
|
+
const response = await this._callSingle("/api/get-selection", {}, void 0, instance_id);
|
|
1837
3276
|
return {
|
|
1838
3277
|
content: [
|
|
1839
3278
|
{
|
|
@@ -1843,11 +3282,11 @@ ${code}`
|
|
|
1843
3282
|
]
|
|
1844
3283
|
};
|
|
1845
3284
|
}
|
|
1846
|
-
async executeLuau(code, target) {
|
|
3285
|
+
async executeLuau(code, target, instance_id) {
|
|
1847
3286
|
if (!code) {
|
|
1848
3287
|
throw new Error("Code is required for execute_luau");
|
|
1849
3288
|
}
|
|
1850
|
-
const response = await this.
|
|
3289
|
+
const response = await this._callSingle("/api/execute-luau", { code }, target || "edit", instance_id);
|
|
1851
3290
|
return {
|
|
1852
3291
|
content: [
|
|
1853
3292
|
{
|
|
@@ -1857,17 +3296,17 @@ ${code}`
|
|
|
1857
3296
|
]
|
|
1858
3297
|
};
|
|
1859
3298
|
}
|
|
1860
|
-
async evalServerRuntime(code) {
|
|
3299
|
+
async evalServerRuntime(code, instance_id) {
|
|
1861
3300
|
if (!code) {
|
|
1862
3301
|
throw new Error("Code is required for eval_server_runtime");
|
|
1863
3302
|
}
|
|
1864
3303
|
const wrapper = buildModuleScriptInvokeWrapper({
|
|
1865
3304
|
service: "ServerScriptService",
|
|
1866
3305
|
bridgeName: SERVER_LOCAL_NAME,
|
|
1867
|
-
missingError: "ServerEvalBridge not
|
|
3306
|
+
missingError: "ServerEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
|
|
1868
3307
|
userCode: code
|
|
1869
3308
|
});
|
|
1870
|
-
const response = await this.
|
|
3309
|
+
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, "server", instance_id);
|
|
1871
3310
|
return {
|
|
1872
3311
|
content: [
|
|
1873
3312
|
{
|
|
@@ -1877,7 +3316,7 @@ ${code}`
|
|
|
1877
3316
|
]
|
|
1878
3317
|
};
|
|
1879
3318
|
}
|
|
1880
|
-
async evalClientRuntime(code, target) {
|
|
3319
|
+
async evalClientRuntime(code, target, instance_id) {
|
|
1881
3320
|
if (!code) {
|
|
1882
3321
|
throw new Error("Code is required for eval_client_runtime");
|
|
1883
3322
|
}
|
|
@@ -1888,10 +3327,10 @@ ${code}`
|
|
|
1888
3327
|
const wrapper = buildModuleScriptInvokeWrapper({
|
|
1889
3328
|
service: "ReplicatedStorage",
|
|
1890
3329
|
bridgeName: CLIENT_LOCAL_NAME,
|
|
1891
|
-
missingError: "ClientEvalBridge not
|
|
3330
|
+
missingError: "ClientEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
|
|
1892
3331
|
userCode: code
|
|
1893
3332
|
});
|
|
1894
|
-
const response = await this.
|
|
3333
|
+
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, clientTarget, instance_id);
|
|
1895
3334
|
return {
|
|
1896
3335
|
content: [
|
|
1897
3336
|
{
|
|
@@ -1901,7 +3340,7 @@ ${code}`
|
|
|
1901
3340
|
]
|
|
1902
3341
|
};
|
|
1903
3342
|
}
|
|
1904
|
-
async getRuntimeLogs(target, since, tail, filter) {
|
|
3343
|
+
async getRuntimeLogs(target, since, tail, filter, instance_id) {
|
|
1905
3344
|
const tgt = target ?? "all";
|
|
1906
3345
|
const data = {};
|
|
1907
3346
|
if (since !== void 0)
|
|
@@ -1910,16 +3349,26 @@ ${code}`
|
|
|
1910
3349
|
data.tail = tail;
|
|
1911
3350
|
if (filter !== void 0)
|
|
1912
3351
|
data.filter = filter;
|
|
1913
|
-
|
|
1914
|
-
|
|
3352
|
+
const resolved = this.bridge.resolveTarget({ instance_id, target: tgt });
|
|
3353
|
+
if (!resolved.ok)
|
|
3354
|
+
throw new RoutingFailure(resolved.error);
|
|
3355
|
+
if (resolved.mode === "single") {
|
|
3356
|
+
const response = await this.client.request("/api/get-runtime-logs", data, resolved.targetInstanceId, resolved.targetRole);
|
|
3357
|
+
response.peer = resolved.targetRole;
|
|
3358
|
+
if (Array.isArray(response.entries)) {
|
|
3359
|
+
for (const e of response.entries) {
|
|
3360
|
+
if (e.peer !== void 0)
|
|
3361
|
+
e.peer = resolved.targetRole;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
1915
3364
|
return {
|
|
1916
3365
|
content: [{ type: "text", text: JSON.stringify(response) }]
|
|
1917
3366
|
};
|
|
1918
3367
|
}
|
|
1919
|
-
const targets =
|
|
3368
|
+
const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
|
|
1920
3369
|
const responses = await Promise.allSettled(targets.map(async (t) => {
|
|
1921
|
-
const r = await this.client.request("/api/get-runtime-logs", data, t);
|
|
1922
|
-
return { ...r, peer: t };
|
|
3370
|
+
const r = await this.client.request("/api/get-runtime-logs", data, t.targetInstanceId, t.targetRole);
|
|
3371
|
+
return { ...r, peer: t.targetRole };
|
|
1923
3372
|
}));
|
|
1924
3373
|
const merged = [];
|
|
1925
3374
|
const perPeerNextSince = {};
|
|
@@ -1965,7 +3414,7 @@ ${code}`
|
|
|
1965
3414
|
content: [{ type: "text", text: JSON.stringify(body) }]
|
|
1966
3415
|
};
|
|
1967
3416
|
}
|
|
1968
|
-
async startPlaytest(mode, numPlayers) {
|
|
3417
|
+
async startPlaytest(mode, numPlayers, instance_id) {
|
|
1969
3418
|
if (mode !== "play" && mode !== "run") {
|
|
1970
3419
|
throw new Error('mode must be "play" or "run"');
|
|
1971
3420
|
}
|
|
@@ -1973,7 +3422,7 @@ ${code}`
|
|
|
1973
3422
|
if (numPlayers !== void 0) {
|
|
1974
3423
|
data.numPlayers = numPlayers;
|
|
1975
3424
|
}
|
|
1976
|
-
const response = await this.
|
|
3425
|
+
const response = await this._callSingle("/api/start-playtest", data, void 0, instance_id);
|
|
1977
3426
|
return {
|
|
1978
3427
|
content: [
|
|
1979
3428
|
{
|
|
@@ -1983,14 +3432,14 @@ ${code}`
|
|
|
1983
3432
|
]
|
|
1984
3433
|
};
|
|
1985
3434
|
}
|
|
1986
|
-
async stopPlaytest() {
|
|
1987
|
-
const response = await this.
|
|
3435
|
+
async stopPlaytest(instance_id) {
|
|
3436
|
+
const response = await this._callSingle("/api/stop-playtest", {}, "edit", instance_id);
|
|
1988
3437
|
return {
|
|
1989
3438
|
content: [{ type: "text", text: JSON.stringify(response) }]
|
|
1990
3439
|
};
|
|
1991
3440
|
}
|
|
1992
|
-
async getPlaytestOutput(target) {
|
|
1993
|
-
const response = await this.
|
|
3441
|
+
async getPlaytestOutput(target, instance_id) {
|
|
3442
|
+
const response = await this._callSingle("/api/get-playtest-output", {}, target || "edit", instance_id);
|
|
1994
3443
|
return {
|
|
1995
3444
|
content: [
|
|
1996
3445
|
{
|
|
@@ -2001,7 +3450,7 @@ ${code}`
|
|
|
2001
3450
|
};
|
|
2002
3451
|
}
|
|
2003
3452
|
async getConnectedInstances() {
|
|
2004
|
-
const instances = this.bridge.
|
|
3453
|
+
const instances = this.bridge.getPublicInstances();
|
|
2005
3454
|
return {
|
|
2006
3455
|
content: [
|
|
2007
3456
|
{
|
|
@@ -2011,8 +3460,8 @@ ${code}`
|
|
|
2011
3460
|
]
|
|
2012
3461
|
};
|
|
2013
3462
|
}
|
|
2014
|
-
async undo() {
|
|
2015
|
-
const response = await this.
|
|
3463
|
+
async undo(instance_id) {
|
|
3464
|
+
const response = await this._callSingle("/api/undo", {}, void 0, instance_id);
|
|
2016
3465
|
return {
|
|
2017
3466
|
content: [
|
|
2018
3467
|
{
|
|
@@ -2022,8 +3471,8 @@ ${code}`
|
|
|
2022
3471
|
]
|
|
2023
3472
|
};
|
|
2024
3473
|
}
|
|
2025
|
-
async redo() {
|
|
2026
|
-
const response = await this.
|
|
3474
|
+
async redo(instance_id) {
|
|
3475
|
+
const response = await this._callSingle("/api/redo", {}, void 0, instance_id);
|
|
2027
3476
|
return {
|
|
2028
3477
|
content: [
|
|
2029
3478
|
{
|
|
@@ -2109,15 +3558,15 @@ ${code}`
|
|
|
2109
3558
|
_RobloxStudioTools._cachedLibraryPath = result;
|
|
2110
3559
|
return result;
|
|
2111
3560
|
}
|
|
2112
|
-
async exportBuild(instancePath, outputId, style = "misc") {
|
|
3561
|
+
async exportBuild(instancePath, outputId, style = "misc", instance_id) {
|
|
2113
3562
|
if (!instancePath) {
|
|
2114
3563
|
throw new Error("Instance path is required for export_build");
|
|
2115
3564
|
}
|
|
2116
|
-
const response = await this.
|
|
3565
|
+
const response = await this._callSingle("/api/export-build", {
|
|
2117
3566
|
instancePath,
|
|
2118
3567
|
outputId,
|
|
2119
3568
|
style
|
|
2120
|
-
});
|
|
3569
|
+
}, void 0, instance_id);
|
|
2121
3570
|
if (response && response.success && response.buildData) {
|
|
2122
3571
|
const buildData = response.buildData;
|
|
2123
3572
|
const buildId = buildData.id || `${style}/exported`;
|
|
@@ -2305,7 +3754,7 @@ ${code}`
|
|
|
2305
3754
|
]
|
|
2306
3755
|
};
|
|
2307
3756
|
}
|
|
2308
|
-
async importBuild(buildData, targetPath, position) {
|
|
3757
|
+
async importBuild(buildData, targetPath, position, instance_id) {
|
|
2309
3758
|
if (!buildData || !targetPath) {
|
|
2310
3759
|
throw new Error("buildData (or library ID string) and targetPath are required for import_build");
|
|
2311
3760
|
}
|
|
@@ -2325,11 +3774,11 @@ ${code}`
|
|
|
2325
3774
|
} else {
|
|
2326
3775
|
resolved = buildData;
|
|
2327
3776
|
}
|
|
2328
|
-
const response = await this.
|
|
3777
|
+
const response = await this._callSingle("/api/import-build", {
|
|
2329
3778
|
buildData: resolved,
|
|
2330
3779
|
targetPath,
|
|
2331
3780
|
position
|
|
2332
|
-
});
|
|
3781
|
+
}, void 0, instance_id);
|
|
2333
3782
|
return {
|
|
2334
3783
|
content: [
|
|
2335
3784
|
{
|
|
@@ -2371,11 +3820,11 @@ ${code}`
|
|
|
2371
3820
|
]
|
|
2372
3821
|
};
|
|
2373
3822
|
}
|
|
2374
|
-
async searchMaterials(query, maxResults) {
|
|
2375
|
-
const response = await this.
|
|
3823
|
+
async searchMaterials(query, maxResults, instance_id) {
|
|
3824
|
+
const response = await this._callSingle("/api/search-materials", {
|
|
2376
3825
|
query: query ?? "",
|
|
2377
3826
|
maxResults: maxResults ?? 50
|
|
2378
|
-
});
|
|
3827
|
+
}, void 0, instance_id);
|
|
2379
3828
|
return {
|
|
2380
3829
|
content: [
|
|
2381
3830
|
{
|
|
@@ -2415,7 +3864,7 @@ ${code}`
|
|
|
2415
3864
|
]
|
|
2416
3865
|
};
|
|
2417
3866
|
}
|
|
2418
|
-
async importScene(sceneData, targetPath = "game.Workspace") {
|
|
3867
|
+
async importScene(sceneData, targetPath = "game.Workspace", instance_id) {
|
|
2419
3868
|
if (!sceneData) {
|
|
2420
3869
|
throw new Error("sceneData is required for import_scene");
|
|
2421
3870
|
}
|
|
@@ -2505,10 +3954,10 @@ ${code}`
|
|
|
2505
3954
|
if (expandedBuilds.length === 0) {
|
|
2506
3955
|
throw new Error("No builds to import - check model references and library");
|
|
2507
3956
|
}
|
|
2508
|
-
const response = await this.
|
|
3957
|
+
const response = await this._callSingle("/api/import-scene", {
|
|
2509
3958
|
expandedBuilds,
|
|
2510
3959
|
targetPath
|
|
2511
|
-
});
|
|
3960
|
+
}, void 0, instance_id);
|
|
2512
3961
|
return {
|
|
2513
3962
|
content: [
|
|
2514
3963
|
{
|
|
@@ -2599,15 +4048,15 @@ ${code}`
|
|
|
2599
4048
|
}]
|
|
2600
4049
|
};
|
|
2601
4050
|
}
|
|
2602
|
-
async insertAsset(assetId, parentPath, position) {
|
|
4051
|
+
async insertAsset(assetId, parentPath, position, instance_id) {
|
|
2603
4052
|
if (!assetId) {
|
|
2604
4053
|
throw new Error("Asset ID is required for insert_asset");
|
|
2605
4054
|
}
|
|
2606
|
-
const response = await this.
|
|
4055
|
+
const response = await this._callSingle("/api/insert-asset", {
|
|
2607
4056
|
assetId,
|
|
2608
4057
|
parentPath: parentPath || "game.Workspace",
|
|
2609
4058
|
position
|
|
2610
|
-
});
|
|
4059
|
+
}, void 0, instance_id);
|
|
2611
4060
|
return {
|
|
2612
4061
|
content: [{
|
|
2613
4062
|
type: "text",
|
|
@@ -2615,15 +4064,15 @@ ${code}`
|
|
|
2615
4064
|
}]
|
|
2616
4065
|
};
|
|
2617
4066
|
}
|
|
2618
|
-
async previewAsset(assetId, includeProperties, maxDepth) {
|
|
4067
|
+
async previewAsset(assetId, includeProperties, maxDepth, instance_id) {
|
|
2619
4068
|
if (!assetId) {
|
|
2620
4069
|
throw new Error("Asset ID is required for preview_asset");
|
|
2621
4070
|
}
|
|
2622
|
-
const response = await this.
|
|
4071
|
+
const response = await this._callSingle("/api/preview-asset", {
|
|
2623
4072
|
assetId,
|
|
2624
4073
|
includeProperties: includeProperties ?? true,
|
|
2625
4074
|
maxDepth: maxDepth ?? 10
|
|
2626
|
-
});
|
|
4075
|
+
}, void 0, instance_id);
|
|
2627
4076
|
return {
|
|
2628
4077
|
content: [{
|
|
2629
4078
|
type: "text",
|
|
@@ -2645,7 +4094,7 @@ ${code}`
|
|
|
2645
4094
|
return id
|
|
2646
4095
|
`;
|
|
2647
4096
|
try {
|
|
2648
|
-
const response = await this.
|
|
4097
|
+
const response = await this._callSingle("/api/execute-luau", { code }, "edit", void 0);
|
|
2649
4098
|
const returnValue = response?.returnValue;
|
|
2650
4099
|
if (returnValue !== void 0 && returnValue !== null && /^\d+$/.test(String(returnValue))) {
|
|
2651
4100
|
return String(returnValue);
|
|
@@ -2720,17 +4169,17 @@ ${code}`
|
|
|
2720
4169
|
}]
|
|
2721
4170
|
};
|
|
2722
4171
|
}
|
|
2723
|
-
async simulateMouseInput(action, x, y, button, scrollDirection, target) {
|
|
4172
|
+
async simulateMouseInput(action, x, y, button, scrollDirection, target, instance_id) {
|
|
2724
4173
|
if (!action) {
|
|
2725
4174
|
throw new Error("action is required for simulate_mouse_input");
|
|
2726
4175
|
}
|
|
2727
|
-
const
|
|
4176
|
+
const { instanceId, clientRole } = this._resolveRuntime(instance_id);
|
|
4177
|
+
const response = await this._callSingle("/api/simulate-mouse-input", {
|
|
2728
4178
|
action,
|
|
2729
4179
|
x,
|
|
2730
4180
|
y,
|
|
2731
|
-
button
|
|
2732
|
-
|
|
2733
|
-
}, target || "edit");
|
|
4181
|
+
button
|
|
4182
|
+
}, target || clientRole || "edit", instanceId);
|
|
2734
4183
|
return {
|
|
2735
4184
|
content: [{
|
|
2736
4185
|
type: "text",
|
|
@@ -2738,15 +4187,17 @@ ${code}`
|
|
|
2738
4187
|
}]
|
|
2739
4188
|
};
|
|
2740
4189
|
}
|
|
2741
|
-
async simulateKeyboardInput(keyCode, action, duration, target) {
|
|
2742
|
-
if (!keyCode) {
|
|
2743
|
-
throw new Error("keyCode is required for simulate_keyboard_input");
|
|
4190
|
+
async simulateKeyboardInput(keyCode, action, duration, text, target, instance_id) {
|
|
4191
|
+
if (!keyCode && text === void 0) {
|
|
4192
|
+
throw new Error("keyCode or text is required for simulate_keyboard_input");
|
|
2744
4193
|
}
|
|
2745
|
-
const
|
|
4194
|
+
const { instanceId, clientRole } = this._resolveRuntime(instance_id);
|
|
4195
|
+
const response = await this._callSingle("/api/simulate-keyboard-input", {
|
|
2746
4196
|
keyCode,
|
|
2747
4197
|
action,
|
|
2748
|
-
duration
|
|
2749
|
-
|
|
4198
|
+
duration,
|
|
4199
|
+
text
|
|
4200
|
+
}, target || clientRole || "edit", instanceId);
|
|
2750
4201
|
return {
|
|
2751
4202
|
content: [{
|
|
2752
4203
|
type: "text",
|
|
@@ -2754,16 +4205,16 @@ ${code}`
|
|
|
2754
4205
|
}]
|
|
2755
4206
|
};
|
|
2756
4207
|
}
|
|
2757
|
-
async characterNavigation(position, instancePath, waitForCompletion, timeout, target) {
|
|
4208
|
+
async characterNavigation(position, instancePath, waitForCompletion, timeout, target, instance_id) {
|
|
2758
4209
|
if (!position && !instancePath) {
|
|
2759
4210
|
throw new Error("Either position or instancePath is required for character_navigation");
|
|
2760
4211
|
}
|
|
2761
|
-
const response = await this.
|
|
4212
|
+
const response = await this._callSingle("/api/character-navigation", {
|
|
2762
4213
|
position,
|
|
2763
4214
|
instancePath,
|
|
2764
4215
|
waitForCompletion,
|
|
2765
4216
|
timeout
|
|
2766
|
-
}, target || "edit");
|
|
4217
|
+
}, target || "edit", instance_id);
|
|
2767
4218
|
return {
|
|
2768
4219
|
content: [{
|
|
2769
4220
|
type: "text",
|
|
@@ -2771,50 +4222,50 @@ ${code}`
|
|
|
2771
4222
|
}]
|
|
2772
4223
|
};
|
|
2773
4224
|
}
|
|
2774
|
-
async cloneObject(instancePath, targetParentPath) {
|
|
4225
|
+
async cloneObject(instancePath, targetParentPath, instance_id) {
|
|
2775
4226
|
if (!instancePath || !targetParentPath) {
|
|
2776
4227
|
throw new Error("instancePath and targetParentPath are required for clone_object");
|
|
2777
4228
|
}
|
|
2778
|
-
const response = await this.
|
|
4229
|
+
const response = await this._callSingle("/api/clone-object", { instancePath, targetParentPath }, void 0, instance_id);
|
|
2779
4230
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2780
4231
|
}
|
|
2781
|
-
async getDescendants(instancePath, maxDepth, classFilter) {
|
|
4232
|
+
async getDescendants(instancePath, maxDepth, classFilter, instance_id) {
|
|
2782
4233
|
if (!instancePath) {
|
|
2783
4234
|
throw new Error("instancePath is required for get_descendants");
|
|
2784
4235
|
}
|
|
2785
|
-
const response = await this.
|
|
4236
|
+
const response = await this._callSingle("/api/get-descendants", { instancePath, maxDepth, classFilter }, void 0, instance_id);
|
|
2786
4237
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2787
4238
|
}
|
|
2788
|
-
async compareInstances(instancePathA, instancePathB) {
|
|
4239
|
+
async compareInstances(instancePathA, instancePathB, instance_id) {
|
|
2789
4240
|
if (!instancePathA || !instancePathB) {
|
|
2790
4241
|
throw new Error("instancePathA and instancePathB are required for compare_instances");
|
|
2791
4242
|
}
|
|
2792
|
-
const response = await this.
|
|
4243
|
+
const response = await this._callSingle("/api/compare-instances", { instancePathA, instancePathB }, void 0, instance_id);
|
|
2793
4244
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2794
4245
|
}
|
|
2795
|
-
async getOutputLog(maxEntries, messageType) {
|
|
2796
|
-
const response = await this.
|
|
4246
|
+
async getOutputLog(maxEntries, messageType, instance_id) {
|
|
4247
|
+
const response = await this._callSingle("/api/get-output-log", { maxEntries, messageType }, void 0, instance_id);
|
|
2797
4248
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2798
4249
|
}
|
|
2799
|
-
async bulkSetAttributes(instancePath, attributes) {
|
|
4250
|
+
async bulkSetAttributes(instancePath, attributes, instance_id) {
|
|
2800
4251
|
if (!instancePath || !attributes) {
|
|
2801
4252
|
throw new Error("instancePath and attributes are required for bulk_set_attributes");
|
|
2802
4253
|
}
|
|
2803
|
-
const response = await this.
|
|
4254
|
+
const response = await this._callSingle("/api/bulk-set-attributes", { instancePath, attributes }, void 0, instance_id);
|
|
2804
4255
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2805
4256
|
}
|
|
2806
|
-
async findAndReplaceInScripts(pattern, replacement, options) {
|
|
4257
|
+
async findAndReplaceInScripts(pattern, replacement, options, instance_id) {
|
|
2807
4258
|
if (!pattern) {
|
|
2808
4259
|
throw new Error("pattern is required for find_and_replace_in_scripts");
|
|
2809
4260
|
}
|
|
2810
4261
|
if (replacement === void 0 || replacement === null) {
|
|
2811
4262
|
throw new Error("replacement is required for find_and_replace_in_scripts");
|
|
2812
4263
|
}
|
|
2813
|
-
const response = await this.
|
|
4264
|
+
const response = await this._callSingle("/api/find-and-replace-in-scripts", {
|
|
2814
4265
|
pattern,
|
|
2815
4266
|
replacement,
|
|
2816
4267
|
...options
|
|
2817
|
-
});
|
|
4268
|
+
}, void 0, instance_id);
|
|
2818
4269
|
return {
|
|
2819
4270
|
content: [{
|
|
2820
4271
|
type: "text",
|
|
@@ -2822,24 +4273,27 @@ ${code}`
|
|
|
2822
4273
|
}]
|
|
2823
4274
|
};
|
|
2824
4275
|
}
|
|
2825
|
-
async getMemoryBreakdown(target, tags) {
|
|
4276
|
+
async getMemoryBreakdown(target, tags, instance_id) {
|
|
2826
4277
|
const tgt = target ?? "all";
|
|
2827
4278
|
const data = {};
|
|
2828
4279
|
if (tags !== void 0)
|
|
2829
4280
|
data.tags = tags;
|
|
2830
|
-
|
|
2831
|
-
|
|
4281
|
+
const resolved = this.bridge.resolveTarget({ instance_id, target: tgt });
|
|
4282
|
+
if (!resolved.ok)
|
|
4283
|
+
throw new RoutingFailure(resolved.error);
|
|
4284
|
+
if (resolved.mode === "single") {
|
|
4285
|
+
const response = await this.client.request("/api/get-memory-breakdown", data, resolved.targetInstanceId, resolved.targetRole);
|
|
2832
4286
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2833
4287
|
}
|
|
2834
|
-
const targets =
|
|
4288
|
+
const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
|
|
2835
4289
|
const responses = await Promise.allSettled(targets.map(async (t) => ({
|
|
2836
|
-
peer: t,
|
|
2837
|
-
result: await this.client.request("/api/get-memory-breakdown", data, t)
|
|
4290
|
+
peer: t.targetRole,
|
|
4291
|
+
result: await this.client.request("/api/get-memory-breakdown", data, t.targetInstanceId, t.targetRole)
|
|
2838
4292
|
})));
|
|
2839
4293
|
const body = {};
|
|
2840
4294
|
for (let i = 0; i < responses.length; i++) {
|
|
2841
4295
|
const r = responses[i];
|
|
2842
|
-
const peer = targets[i];
|
|
4296
|
+
const peer = targets[i].targetRole;
|
|
2843
4297
|
if (r.status === "fulfilled") {
|
|
2844
4298
|
body[peer] = r.value.result;
|
|
2845
4299
|
} else {
|
|
@@ -2848,7 +4302,7 @@ ${code}`
|
|
|
2848
4302
|
}
|
|
2849
4303
|
return { content: [{ type: "text", text: JSON.stringify(body) }] };
|
|
2850
4304
|
}
|
|
2851
|
-
async exportRbxm(instancePaths, outputPath, target) {
|
|
4305
|
+
async exportRbxm(instancePaths, outputPath, target, instance_id) {
|
|
2852
4306
|
if (!Array.isArray(instancePaths) || instancePaths.length === 0) {
|
|
2853
4307
|
throw new Error("instance_paths must be a non-empty array for export_rbxm");
|
|
2854
4308
|
}
|
|
@@ -2859,7 +4313,7 @@ ${code}`
|
|
|
2859
4313
|
if (tgt !== "edit" && tgt !== "server") {
|
|
2860
4314
|
throw new Error(`export_rbxm target must be "edit" or "server" (got: ${tgt})`);
|
|
2861
4315
|
}
|
|
2862
|
-
const response = await this.
|
|
4316
|
+
const response = await this._callSingle("/api/export-rbxm", { instance_paths: instancePaths }, tgt, instance_id);
|
|
2863
4317
|
if (response.error) {
|
|
2864
4318
|
return { content: [{ type: "text", text: JSON.stringify({ error: response.error }) }] };
|
|
2865
4319
|
}
|
|
@@ -2885,7 +4339,7 @@ ${code}`
|
|
|
2885
4339
|
}]
|
|
2886
4340
|
};
|
|
2887
4341
|
}
|
|
2888
|
-
async importRbxm(source, parentPath, target) {
|
|
4342
|
+
async importRbxm(source, parentPath, target, instance_id) {
|
|
2889
4343
|
if (!source || typeof source !== "object") {
|
|
2890
4344
|
throw new Error("source is required for import_rbxm");
|
|
2891
4345
|
}
|
|
@@ -2948,15 +4402,28 @@ ${code}`
|
|
|
2948
4402
|
}
|
|
2949
4403
|
sourceLabel = `base64(${bytes.length}B)`;
|
|
2950
4404
|
}
|
|
2951
|
-
const response = await this.
|
|
4405
|
+
const response = await this._callSingle("/api/import-rbxm", {
|
|
2952
4406
|
base64: bytes.toString("base64"),
|
|
2953
4407
|
parent_path: parentPath,
|
|
2954
4408
|
source_label: sourceLabel
|
|
2955
|
-
}, tgt);
|
|
4409
|
+
}, tgt, instance_id);
|
|
2956
4410
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2957
4411
|
}
|
|
2958
|
-
async captureScreenshot() {
|
|
2959
|
-
const
|
|
4412
|
+
async captureScreenshot(instance_id, format, quality) {
|
|
4413
|
+
const { instanceId, clientRole } = this._resolveRuntime(instance_id);
|
|
4414
|
+
let response;
|
|
4415
|
+
if (clientRole) {
|
|
4416
|
+
const begin = await this._callSingle("/api/capture-begin", {}, clientRole, instanceId);
|
|
4417
|
+
if (begin.error) {
|
|
4418
|
+
return { content: [{ type: "text", text: begin.error }] };
|
|
4419
|
+
}
|
|
4420
|
+
if (!begin.contentId) {
|
|
4421
|
+
return { content: [{ type: "text", text: "Screenshot capture failed: no content id returned from client." }] };
|
|
4422
|
+
}
|
|
4423
|
+
response = await this._callSingle("/api/capture-read", { contentId: begin.contentId }, "edit", instanceId);
|
|
4424
|
+
} else {
|
|
4425
|
+
response = await this._callSingle("/api/capture-screenshot", {}, "edit", instanceId);
|
|
4426
|
+
}
|
|
2960
4427
|
if (response.error) {
|
|
2961
4428
|
return {
|
|
2962
4429
|
content: [{
|
|
@@ -2965,158 +4432,46 @@ ${code}`
|
|
|
2965
4432
|
}]
|
|
2966
4433
|
};
|
|
2967
4434
|
}
|
|
2968
|
-
const
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
BridgeService = class {
|
|
2989
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
2990
|
-
instances = /* @__PURE__ */ new Map();
|
|
2991
|
-
requestTimeout = 3e4;
|
|
2992
|
-
registerInstance(instanceId, role) {
|
|
2993
|
-
let assignedRole = role;
|
|
2994
|
-
if (role === "client") {
|
|
2995
|
-
const used = /* @__PURE__ */ new Set();
|
|
2996
|
-
for (const inst of this.instances.values()) {
|
|
2997
|
-
const match = inst.role.match(/^client-(\d+)$/);
|
|
2998
|
-
if (match)
|
|
2999
|
-
used.add(Number(match[1]));
|
|
4435
|
+
const w = response.width;
|
|
4436
|
+
const h = response.height;
|
|
4437
|
+
if (w === void 0 || h === void 0) {
|
|
4438
|
+
return { content: [{ type: "text", text: "Screenshot response missing dimensions." }] };
|
|
4439
|
+
}
|
|
4440
|
+
const fmt = format === "png" ? "png" : "jpeg";
|
|
4441
|
+
const q = quality === void 0 ? 92 : Math.max(1, Math.min(100, Math.floor(quality)));
|
|
4442
|
+
const MAX_IMAGE_BYTES = 6e6;
|
|
4443
|
+
let { buffer, mimeType } = encodeImageFromRgbaResponse(response, fmt, q);
|
|
4444
|
+
let usedQ = q;
|
|
4445
|
+
let note = "";
|
|
4446
|
+
if (buffer.length > MAX_IMAGE_BYTES) {
|
|
4447
|
+
if (fmt === "png") {
|
|
4448
|
+
const mb = (buffer.length / 1048576).toFixed(1);
|
|
4449
|
+
return {
|
|
4450
|
+
content: [{
|
|
4451
|
+
type: "text",
|
|
4452
|
+
text: `PNG screenshot is ${mb}MB, over the ~${(MAX_IMAGE_BYTES / 1048576).toFixed(0)}MB inline image limit. Use the default jpeg format (optionally with a "quality" value) or make the Studio window smaller for a lossless capture.`
|
|
4453
|
+
}]
|
|
4454
|
+
};
|
|
3000
4455
|
}
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
assignedRole = `client-${idx}`;
|
|
3005
|
-
}
|
|
3006
|
-
this.instances.set(instanceId, {
|
|
3007
|
-
instanceId,
|
|
3008
|
-
role: assignedRole,
|
|
3009
|
-
lastActivity: Date.now(),
|
|
3010
|
-
connectedAt: Date.now()
|
|
3011
|
-
});
|
|
3012
|
-
return assignedRole;
|
|
3013
|
-
}
|
|
3014
|
-
unregisterInstance(instanceId) {
|
|
3015
|
-
this.instances.delete(instanceId);
|
|
3016
|
-
for (const [id, req] of this.pendingRequests.entries()) {
|
|
3017
|
-
const targetRole = req.target;
|
|
3018
|
-
const hasHandler = Array.from(this.instances.values()).some((i) => i.role === targetRole);
|
|
3019
|
-
if (!hasHandler) {
|
|
3020
|
-
clearTimeout(req.timeoutId);
|
|
3021
|
-
this.pendingRequests.delete(id);
|
|
3022
|
-
req.reject(new Error(`Target instance "${targetRole}" disconnected`));
|
|
4456
|
+
while (buffer.length > MAX_IMAGE_BYTES && usedQ > 25) {
|
|
4457
|
+
usedQ = Math.max(25, usedQ - 20);
|
|
4458
|
+
buffer = encodeImageFromRgbaResponse(response, "jpeg", usedQ).buffer;
|
|
3023
4459
|
}
|
|
4460
|
+
note = ` \u2014 auto-reduced to q${usedQ} to fit the inline size limit; enlarge the Studio window or capture a smaller region for finer detail`;
|
|
3024
4461
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
inst.lastActivity = Date.now();
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
cleanupStaleInstances() {
|
|
3039
|
-
const now = Date.now();
|
|
3040
|
-
for (const [id, inst] of this.instances.entries()) {
|
|
3041
|
-
if (now - inst.lastActivity > STALE_INSTANCE_MS) {
|
|
3042
|
-
this.unregisterInstance(id);
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
async sendRequest(endpoint, data, target = "edit") {
|
|
3047
|
-
const requestId = uuidv4();
|
|
3048
|
-
return new Promise((resolve2, reject) => {
|
|
3049
|
-
const timeoutId = setTimeout(() => {
|
|
3050
|
-
if (this.pendingRequests.has(requestId)) {
|
|
3051
|
-
this.pendingRequests.delete(requestId);
|
|
3052
|
-
reject(new Error("Request timeout"));
|
|
3053
|
-
}
|
|
3054
|
-
}, this.requestTimeout);
|
|
3055
|
-
const request = {
|
|
3056
|
-
id: requestId,
|
|
3057
|
-
endpoint,
|
|
3058
|
-
data,
|
|
3059
|
-
target,
|
|
3060
|
-
timestamp: Date.now(),
|
|
3061
|
-
resolve: resolve2,
|
|
3062
|
-
reject,
|
|
3063
|
-
timeoutId
|
|
3064
|
-
};
|
|
3065
|
-
this.pendingRequests.set(requestId, request);
|
|
3066
|
-
});
|
|
3067
|
-
}
|
|
3068
|
-
getPendingRequest(callerRole = "edit") {
|
|
3069
|
-
let oldestRequest = null;
|
|
3070
|
-
for (const request of this.pendingRequests.values()) {
|
|
3071
|
-
if (request.target !== callerRole)
|
|
3072
|
-
continue;
|
|
3073
|
-
if (!oldestRequest || request.timestamp < oldestRequest.timestamp) {
|
|
3074
|
-
oldestRequest = request;
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
if (oldestRequest) {
|
|
3078
|
-
return {
|
|
3079
|
-
requestId: oldestRequest.id,
|
|
3080
|
-
request: {
|
|
3081
|
-
endpoint: oldestRequest.endpoint,
|
|
3082
|
-
data: oldestRequest.data
|
|
4462
|
+
return {
|
|
4463
|
+
content: [
|
|
4464
|
+
{
|
|
4465
|
+
type: "text",
|
|
4466
|
+
text: `Screenshot ${w}x${h}px (${fmt}${fmt === "jpeg" ? ` q${usedQ}` : ""})${note}. For simulate_mouse_input, x/y are pixel coordinates in this exact image with (0,0) at the top-left; it is not downscaled, so use coordinates as you read them off the image.`
|
|
4467
|
+
},
|
|
4468
|
+
{
|
|
4469
|
+
type: "image",
|
|
4470
|
+
data: buffer.toString("base64"),
|
|
4471
|
+
mimeType
|
|
3083
4472
|
}
|
|
3084
|
-
|
|
3085
|
-
}
|
|
3086
|
-
return null;
|
|
3087
|
-
}
|
|
3088
|
-
resolveRequest(requestId, response) {
|
|
3089
|
-
const request = this.pendingRequests.get(requestId);
|
|
3090
|
-
if (request) {
|
|
3091
|
-
clearTimeout(request.timeoutId);
|
|
3092
|
-
this.pendingRequests.delete(requestId);
|
|
3093
|
-
request.resolve(response);
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
rejectRequest(requestId, error) {
|
|
3097
|
-
const request = this.pendingRequests.get(requestId);
|
|
3098
|
-
if (request) {
|
|
3099
|
-
clearTimeout(request.timeoutId);
|
|
3100
|
-
this.pendingRequests.delete(requestId);
|
|
3101
|
-
request.reject(error);
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
cleanupOldRequests() {
|
|
3105
|
-
const now = Date.now();
|
|
3106
|
-
for (const [id, request] of this.pendingRequests.entries()) {
|
|
3107
|
-
if (now - request.timestamp > this.requestTimeout) {
|
|
3108
|
-
clearTimeout(request.timeoutId);
|
|
3109
|
-
this.pendingRequests.delete(id);
|
|
3110
|
-
request.reject(new Error("Request timeout"));
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
clearAllPendingRequests() {
|
|
3115
|
-
for (const [, request] of this.pendingRequests.entries()) {
|
|
3116
|
-
clearTimeout(request.timeoutId);
|
|
3117
|
-
request.reject(new Error("Connection closed"));
|
|
3118
|
-
}
|
|
3119
|
-
this.pendingRequests.clear();
|
|
4473
|
+
]
|
|
4474
|
+
};
|
|
3120
4475
|
}
|
|
3121
4476
|
};
|
|
3122
4477
|
}
|
|
@@ -3166,14 +4521,20 @@ var init_proxy_bridge_service = __esm({
|
|
|
3166
4521
|
this.refreshTimer = void 0;
|
|
3167
4522
|
}
|
|
3168
4523
|
}
|
|
3169
|
-
async sendRequest(endpoint, data,
|
|
4524
|
+
async sendRequest(endpoint, data, targetInstanceId, targetRole) {
|
|
3170
4525
|
const controller = new AbortController();
|
|
3171
4526
|
const timeoutId = setTimeout(() => controller.abort(), this.proxyRequestTimeout);
|
|
3172
4527
|
try {
|
|
3173
4528
|
const response = await fetch(`${this.primaryBaseUrl}/proxy`, {
|
|
3174
4529
|
method: "POST",
|
|
3175
4530
|
headers: { "Content-Type": "application/json" },
|
|
3176
|
-
body: JSON.stringify({
|
|
4531
|
+
body: JSON.stringify({
|
|
4532
|
+
endpoint,
|
|
4533
|
+
data,
|
|
4534
|
+
targetInstanceId,
|
|
4535
|
+
targetRole,
|
|
4536
|
+
proxyInstanceId: this.proxyInstanceId
|
|
4537
|
+
}),
|
|
3177
4538
|
signal: controller.signal
|
|
3178
4539
|
});
|
|
3179
4540
|
clearTimeout(timeoutId);
|
|
@@ -3257,6 +4618,19 @@ var init_server = __esm({
|
|
|
3257
4618
|
try {
|
|
3258
4619
|
return await handler(this.tools, args ?? {});
|
|
3259
4620
|
} catch (error) {
|
|
4621
|
+
if (error instanceof RoutingFailure) {
|
|
4622
|
+
return {
|
|
4623
|
+
content: [{
|
|
4624
|
+
type: "text",
|
|
4625
|
+
text: JSON.stringify({
|
|
4626
|
+
error: error.routingError.code,
|
|
4627
|
+
message: error.routingError.message,
|
|
4628
|
+
data: error.routingError.data
|
|
4629
|
+
})
|
|
4630
|
+
}],
|
|
4631
|
+
isError: true
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
3260
4634
|
if (error instanceof McpError2)
|
|
3261
4635
|
throw error;
|
|
3262
4636
|
throw new McpError2(ErrorCode2.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -3398,6 +4772,10 @@ var init_definitions = __esm({
|
|
|
3398
4772
|
path: {
|
|
3399
4773
|
type: "string",
|
|
3400
4774
|
description: "Root path (default: game root)"
|
|
4775
|
+
},
|
|
4776
|
+
instance_id: {
|
|
4777
|
+
type: "string",
|
|
4778
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3401
4779
|
}
|
|
3402
4780
|
}
|
|
3403
4781
|
}
|
|
@@ -3417,6 +4795,10 @@ var init_definitions = __esm({
|
|
|
3417
4795
|
type: "string",
|
|
3418
4796
|
enum: ["name", "type", "content"],
|
|
3419
4797
|
description: "Search mode (default: name)"
|
|
4798
|
+
},
|
|
4799
|
+
instance_id: {
|
|
4800
|
+
type: "string",
|
|
4801
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3420
4802
|
}
|
|
3421
4803
|
},
|
|
3422
4804
|
required: ["query"]
|
|
@@ -3429,7 +4811,12 @@ var init_definitions = __esm({
|
|
|
3429
4811
|
description: "Get place ID, name, and game settings",
|
|
3430
4812
|
inputSchema: {
|
|
3431
4813
|
type: "object",
|
|
3432
|
-
properties: {
|
|
4814
|
+
properties: {
|
|
4815
|
+
instance_id: {
|
|
4816
|
+
type: "string",
|
|
4817
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
3433
4820
|
}
|
|
3434
4821
|
},
|
|
3435
4822
|
{
|
|
@@ -3442,6 +4829,10 @@ var init_definitions = __esm({
|
|
|
3442
4829
|
serviceName: {
|
|
3443
4830
|
type: "string",
|
|
3444
4831
|
description: "Specific service name"
|
|
4832
|
+
},
|
|
4833
|
+
instance_id: {
|
|
4834
|
+
type: "string",
|
|
4835
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3445
4836
|
}
|
|
3446
4837
|
}
|
|
3447
4838
|
}
|
|
@@ -3465,6 +4856,10 @@ var init_definitions = __esm({
|
|
|
3465
4856
|
propertyName: {
|
|
3466
4857
|
type: "string",
|
|
3467
4858
|
description: 'Property name when searchType is "property"'
|
|
4859
|
+
},
|
|
4860
|
+
instance_id: {
|
|
4861
|
+
type: "string",
|
|
4862
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3468
4863
|
}
|
|
3469
4864
|
},
|
|
3470
4865
|
required: ["query"]
|
|
@@ -3485,6 +4880,10 @@ var init_definitions = __esm({
|
|
|
3485
4880
|
excludeSource: {
|
|
3486
4881
|
type: "boolean",
|
|
3487
4882
|
description: "For scripts, return SourceLength/LineCount instead of full source (default: false)"
|
|
4883
|
+
},
|
|
4884
|
+
instance_id: {
|
|
4885
|
+
type: "string",
|
|
4886
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3488
4887
|
}
|
|
3489
4888
|
},
|
|
3490
4889
|
required: ["instancePath"]
|
|
@@ -3500,6 +4899,10 @@ var init_definitions = __esm({
|
|
|
3500
4899
|
instancePath: {
|
|
3501
4900
|
type: "string",
|
|
3502
4901
|
description: "Instance path (dot notation)"
|
|
4902
|
+
},
|
|
4903
|
+
instance_id: {
|
|
4904
|
+
type: "string",
|
|
4905
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3503
4906
|
}
|
|
3504
4907
|
},
|
|
3505
4908
|
required: ["instancePath"]
|
|
@@ -3519,6 +4922,10 @@ var init_definitions = __esm({
|
|
|
3519
4922
|
propertyValue: {
|
|
3520
4923
|
type: "string",
|
|
3521
4924
|
description: "Value to match"
|
|
4925
|
+
},
|
|
4926
|
+
instance_id: {
|
|
4927
|
+
type: "string",
|
|
4928
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3522
4929
|
}
|
|
3523
4930
|
},
|
|
3524
4931
|
required: ["propertyName", "propertyValue"]
|
|
@@ -3534,6 +4941,10 @@ var init_definitions = __esm({
|
|
|
3534
4941
|
className: {
|
|
3535
4942
|
type: "string",
|
|
3536
4943
|
description: "Roblox class name"
|
|
4944
|
+
},
|
|
4945
|
+
instance_id: {
|
|
4946
|
+
type: "string",
|
|
4947
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3537
4948
|
}
|
|
3538
4949
|
},
|
|
3539
4950
|
required: ["className"]
|
|
@@ -3558,6 +4969,10 @@ var init_definitions = __esm({
|
|
|
3558
4969
|
scriptsOnly: {
|
|
3559
4970
|
type: "boolean",
|
|
3560
4971
|
description: "Show only scripts (default: false)"
|
|
4972
|
+
},
|
|
4973
|
+
instance_id: {
|
|
4974
|
+
type: "string",
|
|
4975
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3561
4976
|
}
|
|
3562
4977
|
}
|
|
3563
4978
|
}
|
|
@@ -3580,6 +4995,10 @@ var init_definitions = __esm({
|
|
|
3580
4995
|
},
|
|
3581
4996
|
propertyValue: {
|
|
3582
4997
|
description: "Value to set (string, number, boolean, or object for Vector3/Color3/UDim2)"
|
|
4998
|
+
},
|
|
4999
|
+
instance_id: {
|
|
5000
|
+
type: "string",
|
|
5001
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3583
5002
|
}
|
|
3584
5003
|
},
|
|
3585
5004
|
required: ["instancePath", "propertyName", "propertyValue"]
|
|
@@ -3603,6 +5022,10 @@ var init_definitions = __esm({
|
|
|
3603
5022
|
},
|
|
3604
5023
|
propertyValue: {
|
|
3605
5024
|
description: "Value to set (string, number, boolean, or object for Vector3/Color3/UDim2)"
|
|
5025
|
+
},
|
|
5026
|
+
instance_id: {
|
|
5027
|
+
type: "string",
|
|
5028
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3606
5029
|
}
|
|
3607
5030
|
},
|
|
3608
5031
|
required: ["paths", "propertyName", "propertyValue"]
|
|
@@ -3623,6 +5046,10 @@ var init_definitions = __esm({
|
|
|
3623
5046
|
propertyName: {
|
|
3624
5047
|
type: "string",
|
|
3625
5048
|
description: "Property name"
|
|
5049
|
+
},
|
|
5050
|
+
instance_id: {
|
|
5051
|
+
type: "string",
|
|
5052
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3626
5053
|
}
|
|
3627
5054
|
},
|
|
3628
5055
|
required: ["paths", "propertyName"]
|
|
@@ -3642,6 +5069,10 @@ var init_definitions = __esm({
|
|
|
3642
5069
|
properties: {
|
|
3643
5070
|
type: "object",
|
|
3644
5071
|
description: "Map of property name to value"
|
|
5072
|
+
},
|
|
5073
|
+
instance_id: {
|
|
5074
|
+
type: "string",
|
|
5075
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3645
5076
|
}
|
|
3646
5077
|
},
|
|
3647
5078
|
required: ["instancePath", "properties"]
|
|
@@ -3670,6 +5101,10 @@ var init_definitions = __esm({
|
|
|
3670
5101
|
properties: {
|
|
3671
5102
|
type: "object",
|
|
3672
5103
|
description: "Properties to set on creation"
|
|
5104
|
+
},
|
|
5105
|
+
instance_id: {
|
|
5106
|
+
type: "string",
|
|
5107
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3673
5108
|
}
|
|
3674
5109
|
},
|
|
3675
5110
|
required: ["className", "parent"]
|
|
@@ -3707,6 +5142,10 @@ var init_definitions = __esm({
|
|
|
3707
5142
|
required: ["className", "parent"]
|
|
3708
5143
|
},
|
|
3709
5144
|
description: "Objects to create"
|
|
5145
|
+
},
|
|
5146
|
+
instance_id: {
|
|
5147
|
+
type: "string",
|
|
5148
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3710
5149
|
}
|
|
3711
5150
|
},
|
|
3712
5151
|
required: ["objects"]
|
|
@@ -3722,6 +5161,10 @@ var init_definitions = __esm({
|
|
|
3722
5161
|
instancePath: {
|
|
3723
5162
|
type: "string",
|
|
3724
5163
|
description: "Instance path (dot notation)"
|
|
5164
|
+
},
|
|
5165
|
+
instance_id: {
|
|
5166
|
+
type: "string",
|
|
5167
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3725
5168
|
}
|
|
3726
5169
|
},
|
|
3727
5170
|
required: ["instancePath"]
|
|
@@ -3775,6 +5218,10 @@ var init_definitions = __esm({
|
|
|
3775
5218
|
description: "Different parent per duplicate"
|
|
3776
5219
|
}
|
|
3777
5220
|
}
|
|
5221
|
+
},
|
|
5222
|
+
instance_id: {
|
|
5223
|
+
type: "string",
|
|
5224
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3778
5225
|
}
|
|
3779
5226
|
},
|
|
3780
5227
|
required: ["instancePath", "count"]
|
|
@@ -3837,6 +5284,10 @@ var init_definitions = __esm({
|
|
|
3837
5284
|
required: ["instancePath", "count"]
|
|
3838
5285
|
},
|
|
3839
5286
|
description: "Duplication operations"
|
|
5287
|
+
},
|
|
5288
|
+
instance_id: {
|
|
5289
|
+
type: "string",
|
|
5290
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3840
5291
|
}
|
|
3841
5292
|
},
|
|
3842
5293
|
required: ["duplications"]
|
|
@@ -3862,6 +5313,10 @@ var init_definitions = __esm({
|
|
|
3862
5313
|
endLine: {
|
|
3863
5314
|
type: "number",
|
|
3864
5315
|
description: "End line (inclusive)"
|
|
5316
|
+
},
|
|
5317
|
+
instance_id: {
|
|
5318
|
+
type: "string",
|
|
5319
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3865
5320
|
}
|
|
3866
5321
|
},
|
|
3867
5322
|
required: ["instancePath"]
|
|
@@ -3881,6 +5336,10 @@ var init_definitions = __esm({
|
|
|
3881
5336
|
source: {
|
|
3882
5337
|
type: "string",
|
|
3883
5338
|
description: "New source code"
|
|
5339
|
+
},
|
|
5340
|
+
instance_id: {
|
|
5341
|
+
type: "string",
|
|
5342
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3884
5343
|
}
|
|
3885
5344
|
},
|
|
3886
5345
|
required: ["instancePath", "source"]
|
|
@@ -3908,6 +5367,10 @@ var init_definitions = __esm({
|
|
|
3908
5367
|
startLine: {
|
|
3909
5368
|
type: "number",
|
|
3910
5369
|
description: "Optional 1-indexed line where old_string begins. When provided, skips uniqueness check and requires old_string to match starting at that exact line."
|
|
5370
|
+
},
|
|
5371
|
+
instance_id: {
|
|
5372
|
+
type: "string",
|
|
5373
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3911
5374
|
}
|
|
3912
5375
|
},
|
|
3913
5376
|
required: ["instancePath", "old_string", "new_string"]
|
|
@@ -3931,6 +5394,10 @@ var init_definitions = __esm({
|
|
|
3931
5394
|
newContent: {
|
|
3932
5395
|
type: "string",
|
|
3933
5396
|
description: "Content to insert"
|
|
5397
|
+
},
|
|
5398
|
+
instance_id: {
|
|
5399
|
+
type: "string",
|
|
5400
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3934
5401
|
}
|
|
3935
5402
|
},
|
|
3936
5403
|
required: ["instancePath", "newContent"]
|
|
@@ -3954,6 +5421,10 @@ var init_definitions = __esm({
|
|
|
3954
5421
|
endLine: {
|
|
3955
5422
|
type: "number",
|
|
3956
5423
|
description: "End line (inclusive)"
|
|
5424
|
+
},
|
|
5425
|
+
instance_id: {
|
|
5426
|
+
type: "string",
|
|
5427
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3957
5428
|
}
|
|
3958
5429
|
},
|
|
3959
5430
|
required: ["instancePath", "startLine", "endLine"]
|
|
@@ -3981,6 +5452,10 @@ var init_definitions = __esm({
|
|
|
3981
5452
|
valueType: {
|
|
3982
5453
|
type: "string",
|
|
3983
5454
|
description: "Type hint if needed"
|
|
5455
|
+
},
|
|
5456
|
+
instance_id: {
|
|
5457
|
+
type: "string",
|
|
5458
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3984
5459
|
}
|
|
3985
5460
|
},
|
|
3986
5461
|
required: ["instancePath", "attributeName", "attributeValue"]
|
|
@@ -3996,6 +5471,10 @@ var init_definitions = __esm({
|
|
|
3996
5471
|
instancePath: {
|
|
3997
5472
|
type: "string",
|
|
3998
5473
|
description: "Instance path (dot notation)"
|
|
5474
|
+
},
|
|
5475
|
+
instance_id: {
|
|
5476
|
+
type: "string",
|
|
5477
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3999
5478
|
}
|
|
4000
5479
|
},
|
|
4001
5480
|
required: ["instancePath"]
|
|
@@ -4015,6 +5494,10 @@ var init_definitions = __esm({
|
|
|
4015
5494
|
attributeName: {
|
|
4016
5495
|
type: "string",
|
|
4017
5496
|
description: "Attribute name"
|
|
5497
|
+
},
|
|
5498
|
+
instance_id: {
|
|
5499
|
+
type: "string",
|
|
5500
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4018
5501
|
}
|
|
4019
5502
|
},
|
|
4020
5503
|
required: ["instancePath", "attributeName"]
|
|
@@ -4031,6 +5514,10 @@ var init_definitions = __esm({
|
|
|
4031
5514
|
instancePath: {
|
|
4032
5515
|
type: "string",
|
|
4033
5516
|
description: "Instance path (dot notation)"
|
|
5517
|
+
},
|
|
5518
|
+
instance_id: {
|
|
5519
|
+
type: "string",
|
|
5520
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4034
5521
|
}
|
|
4035
5522
|
},
|
|
4036
5523
|
required: ["instancePath"]
|
|
@@ -4050,6 +5537,10 @@ var init_definitions = __esm({
|
|
|
4050
5537
|
tagName: {
|
|
4051
5538
|
type: "string",
|
|
4052
5539
|
description: "Tag name"
|
|
5540
|
+
},
|
|
5541
|
+
instance_id: {
|
|
5542
|
+
type: "string",
|
|
5543
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4053
5544
|
}
|
|
4054
5545
|
},
|
|
4055
5546
|
required: ["instancePath", "tagName"]
|
|
@@ -4069,6 +5560,10 @@ var init_definitions = __esm({
|
|
|
4069
5560
|
tagName: {
|
|
4070
5561
|
type: "string",
|
|
4071
5562
|
description: "Tag name"
|
|
5563
|
+
},
|
|
5564
|
+
instance_id: {
|
|
5565
|
+
type: "string",
|
|
5566
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4072
5567
|
}
|
|
4073
5568
|
},
|
|
4074
5569
|
required: ["instancePath", "tagName"]
|
|
@@ -4084,6 +5579,10 @@ var init_definitions = __esm({
|
|
|
4084
5579
|
tagName: {
|
|
4085
5580
|
type: "string",
|
|
4086
5581
|
description: "Tag name"
|
|
5582
|
+
},
|
|
5583
|
+
instance_id: {
|
|
5584
|
+
type: "string",
|
|
5585
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4087
5586
|
}
|
|
4088
5587
|
},
|
|
4089
5588
|
required: ["tagName"]
|
|
@@ -4096,7 +5595,12 @@ var init_definitions = __esm({
|
|
|
4096
5595
|
description: "Get all currently selected objects",
|
|
4097
5596
|
inputSchema: {
|
|
4098
5597
|
type: "object",
|
|
4099
|
-
properties: {
|
|
5598
|
+
properties: {
|
|
5599
|
+
instance_id: {
|
|
5600
|
+
type: "string",
|
|
5601
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
4100
5604
|
}
|
|
4101
5605
|
},
|
|
4102
5606
|
// === Luau Execution ===
|
|
@@ -4114,6 +5618,10 @@ var init_definitions = __esm({
|
|
|
4114
5618
|
target: {
|
|
4115
5619
|
type: "string",
|
|
4116
5620
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
5621
|
+
},
|
|
5622
|
+
instance_id: {
|
|
5623
|
+
type: "string",
|
|
5624
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4117
5625
|
}
|
|
4118
5626
|
},
|
|
4119
5627
|
required: ["code"]
|
|
@@ -4122,13 +5630,17 @@ var init_definitions = __esm({
|
|
|
4122
5630
|
{
|
|
4123
5631
|
name: "eval_server_runtime",
|
|
4124
5632
|
category: "write",
|
|
4125
|
-
description: "Execute Luau on the server peer in the running game's Script VM (shares require cache with user game scripts). Use this instead of execute_luau target=server when you need to see runtime-mutated module state.
|
|
5633
|
+
description: "Execute Luau on the server peer in the running game's Script VM (shares require cache with user game scripts). Use this instead of execute_luau target=server when you need to see runtime-mutated module state. Requires a running playtest; the bridge is installed automatically (including for playtests started manually via the Studio Play button).",
|
|
4126
5634
|
inputSchema: {
|
|
4127
5635
|
type: "object",
|
|
4128
5636
|
properties: {
|
|
4129
5637
|
code: {
|
|
4130
5638
|
type: "string",
|
|
4131
5639
|
description: "Luau code to execute. Use return ... to get a value back."
|
|
5640
|
+
},
|
|
5641
|
+
instance_id: {
|
|
5642
|
+
type: "string",
|
|
5643
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4132
5644
|
}
|
|
4133
5645
|
},
|
|
4134
5646
|
required: ["code"]
|
|
@@ -4137,7 +5649,7 @@ var init_definitions = __esm({
|
|
|
4137
5649
|
{
|
|
4138
5650
|
name: "eval_client_runtime",
|
|
4139
5651
|
category: "write",
|
|
4140
|
-
description: "Execute Luau on a client peer in the running game's LocalScript VM (shares require cache with user game scripts). Use this instead of execute_luau target=client-N when you need to see runtime-mutated module state.
|
|
5652
|
+
description: "Execute Luau on a client peer in the running game's LocalScript VM (shares require cache with user game scripts). Use this instead of execute_luau target=client-N when you need to see runtime-mutated module state. Requires a running playtest; the bridge is installed automatically (including for playtests started manually via the Studio Play button).",
|
|
4141
5653
|
inputSchema: {
|
|
4142
5654
|
type: "object",
|
|
4143
5655
|
properties: {
|
|
@@ -4148,6 +5660,10 @@ var init_definitions = __esm({
|
|
|
4148
5660
|
target: {
|
|
4149
5661
|
type: "string",
|
|
4150
5662
|
description: 'Client target: "client-1" (default), "client-2", etc.'
|
|
5663
|
+
},
|
|
5664
|
+
instance_id: {
|
|
5665
|
+
type: "string",
|
|
5666
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4151
5667
|
}
|
|
4152
5668
|
},
|
|
4153
5669
|
required: ["code"]
|
|
@@ -4197,6 +5713,10 @@ var init_definitions = __esm({
|
|
|
4197
5713
|
type: "string",
|
|
4198
5714
|
enum: ["Script", "LocalScript", "ModuleScript"],
|
|
4199
5715
|
description: "Only search scripts of this class type"
|
|
5716
|
+
},
|
|
5717
|
+
instance_id: {
|
|
5718
|
+
type: "string",
|
|
5719
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4200
5720
|
}
|
|
4201
5721
|
},
|
|
4202
5722
|
required: ["pattern"]
|
|
@@ -4218,6 +5738,10 @@ var init_definitions = __esm({
|
|
|
4218
5738
|
numPlayers: {
|
|
4219
5739
|
type: "number",
|
|
4220
5740
|
description: "Number of client players (1-8). Triggers server + clients mode via TestService."
|
|
5741
|
+
},
|
|
5742
|
+
instance_id: {
|
|
5743
|
+
type: "string",
|
|
5744
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4221
5745
|
}
|
|
4222
5746
|
},
|
|
4223
5747
|
required: ["mode"]
|
|
@@ -4229,7 +5753,12 @@ var init_definitions = __esm({
|
|
|
4229
5753
|
description: "Stop playtest and return all captured output.",
|
|
4230
5754
|
inputSchema: {
|
|
4231
5755
|
type: "object",
|
|
4232
|
-
properties: {
|
|
5756
|
+
properties: {
|
|
5757
|
+
instance_id: {
|
|
5758
|
+
type: "string",
|
|
5759
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
4233
5762
|
}
|
|
4234
5763
|
},
|
|
4235
5764
|
{
|
|
@@ -4242,6 +5771,10 @@ var init_definitions = __esm({
|
|
|
4242
5771
|
target: {
|
|
4243
5772
|
type: "string",
|
|
4244
5773
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
5774
|
+
},
|
|
5775
|
+
instance_id: {
|
|
5776
|
+
type: "string",
|
|
5777
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4245
5778
|
}
|
|
4246
5779
|
}
|
|
4247
5780
|
}
|
|
@@ -4268,6 +5801,10 @@ var init_definitions = __esm({
|
|
|
4268
5801
|
filter: {
|
|
4269
5802
|
type: "string",
|
|
4270
5803
|
description: "Plain substring matched against each entry's message (no pattern semantics; literal text). Applied after since, before tail."
|
|
5804
|
+
},
|
|
5805
|
+
instance_id: {
|
|
5806
|
+
type: "string",
|
|
5807
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4271
5808
|
}
|
|
4272
5809
|
}
|
|
4273
5810
|
}
|
|
@@ -4289,7 +5826,12 @@ var init_definitions = __esm({
|
|
|
4289
5826
|
description: "Undo the last change in Roblox Studio. Uses ChangeHistoryService to reverse the most recent operation.",
|
|
4290
5827
|
inputSchema: {
|
|
4291
5828
|
type: "object",
|
|
4292
|
-
properties: {
|
|
5829
|
+
properties: {
|
|
5830
|
+
instance_id: {
|
|
5831
|
+
type: "string",
|
|
5832
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
4293
5835
|
}
|
|
4294
5836
|
},
|
|
4295
5837
|
{
|
|
@@ -4298,7 +5840,12 @@ var init_definitions = __esm({
|
|
|
4298
5840
|
description: "Redo the last undone change in Roblox Studio. Uses ChangeHistoryService to reapply the most recently undone operation.",
|
|
4299
5841
|
inputSchema: {
|
|
4300
5842
|
type: "object",
|
|
4301
|
-
properties: {
|
|
5843
|
+
properties: {
|
|
5844
|
+
instance_id: {
|
|
5845
|
+
type: "string",
|
|
5846
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
4302
5849
|
}
|
|
4303
5850
|
},
|
|
4304
5851
|
// === Build Library ===
|
|
@@ -4321,6 +5868,10 @@ var init_definitions = __esm({
|
|
|
4321
5868
|
type: "string",
|
|
4322
5869
|
enum: ["medieval", "modern", "nature", "scifi", "misc"],
|
|
4323
5870
|
description: "Style category for the build (default: misc)"
|
|
5871
|
+
},
|
|
5872
|
+
instance_id: {
|
|
5873
|
+
type: "string",
|
|
5874
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4324
5875
|
}
|
|
4325
5876
|
},
|
|
4326
5877
|
required: ["instancePath"]
|
|
@@ -4470,6 +6021,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4470
6021
|
type: "array",
|
|
4471
6022
|
items: { type: "number" },
|
|
4472
6023
|
description: "World position offset [X, Y, Z]"
|
|
6024
|
+
},
|
|
6025
|
+
instance_id: {
|
|
6026
|
+
type: "string",
|
|
6027
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4473
6028
|
}
|
|
4474
6029
|
},
|
|
4475
6030
|
required: ["buildData", "targetPath"]
|
|
@@ -4504,6 +6059,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4504
6059
|
maxResults: {
|
|
4505
6060
|
type: "number",
|
|
4506
6061
|
description: "Max results to return (default: 50)"
|
|
6062
|
+
},
|
|
6063
|
+
instance_id: {
|
|
6064
|
+
type: "string",
|
|
6065
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4507
6066
|
}
|
|
4508
6067
|
}
|
|
4509
6068
|
}
|
|
@@ -4588,6 +6147,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4588
6147
|
targetPath: {
|
|
4589
6148
|
type: "string",
|
|
4590
6149
|
description: "Parent instance path for the scene (default: game.Workspace)"
|
|
6150
|
+
},
|
|
6151
|
+
instance_id: {
|
|
6152
|
+
type: "string",
|
|
6153
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4591
6154
|
}
|
|
4592
6155
|
},
|
|
4593
6156
|
required: ["sceneData"]
|
|
@@ -4685,6 +6248,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4685
6248
|
z: { type: "number" }
|
|
4686
6249
|
},
|
|
4687
6250
|
description: "Optional world position to place the asset"
|
|
6251
|
+
},
|
|
6252
|
+
instance_id: {
|
|
6253
|
+
type: "string",
|
|
6254
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4688
6255
|
}
|
|
4689
6256
|
},
|
|
4690
6257
|
required: ["assetId"]
|
|
@@ -4708,6 +6275,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4708
6275
|
maxDepth: {
|
|
4709
6276
|
type: "number",
|
|
4710
6277
|
description: "Max hierarchy traversal depth (default: 10)"
|
|
6278
|
+
},
|
|
6279
|
+
instance_id: {
|
|
6280
|
+
type: "string",
|
|
6281
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4711
6282
|
}
|
|
4712
6283
|
},
|
|
4713
6284
|
required: ["assetId"]
|
|
@@ -4752,46 +6323,59 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4752
6323
|
{
|
|
4753
6324
|
name: "capture_screenshot",
|
|
4754
6325
|
category: "read",
|
|
4755
|
-
description: 'Capture
|
|
6326
|
+
description: 'Capture the Roblox Studio viewport at native resolution and return it as an image, plus a text line stating the exact pixel dimensions. Works in Edit mode and during a playtest (auto-detects a running client and captures the live play viewport). The returned image is never downscaled, so its pixel grid is exactly the coordinate space simulate_mouse_input uses \u2014 read click positions straight off this image. For reading fine text/UI, use format="png" (lossless) or a higher quality; enlarging the Studio window raises resolution. Requires EditableImage API enabled (Game Settings > Security > "Allow Mesh / Image APIs") and the window to be visible.',
|
|
4756
6327
|
inputSchema: {
|
|
4757
6328
|
type: "object",
|
|
4758
|
-
properties: {
|
|
6329
|
+
properties: {
|
|
6330
|
+
format: {
|
|
6331
|
+
type: "string",
|
|
6332
|
+
enum: ["jpeg", "png"],
|
|
6333
|
+
description: 'Image format. "jpeg" (default) is compact and crisp at high quality. "png" is lossless \u2014 best for reading dense text/UI, but larger (a busy 3D scene may be big).'
|
|
6334
|
+
},
|
|
6335
|
+
quality: {
|
|
6336
|
+
type: "number",
|
|
6337
|
+
description: "JPEG quality 1-100 (default 92). Higher = sharper text, larger size. Ignored for png."
|
|
6338
|
+
},
|
|
6339
|
+
instance_id: {
|
|
6340
|
+
type: "string",
|
|
6341
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
4759
6344
|
}
|
|
4760
6345
|
},
|
|
4761
6346
|
// === Input Simulation ===
|
|
4762
6347
|
{
|
|
4763
6348
|
name: "simulate_mouse_input",
|
|
4764
6349
|
category: "write",
|
|
4765
|
-
description: "Simulate mouse
|
|
6350
|
+
description: "Simulate a mouse click in the running game via UserInputService:CreateVirtualInput. Use during a playtest to click UI buttons, interact with objects, or aim. Fires real UserInputService input and activates GUI buttons. Coordinates are viewport pixels matching capture_screenshot (top-left is 0,0) \u2014 take a screenshot first to find positions. Auto-targets the running client; only works during a playtest. Note: only click/mouseDown/mouseUp are supported (the API has no mouse-move or scroll).",
|
|
4766
6351
|
inputSchema: {
|
|
4767
6352
|
type: "object",
|
|
4768
6353
|
properties: {
|
|
4769
6354
|
action: {
|
|
4770
6355
|
type: "string",
|
|
4771
|
-
enum: ["click", "mouseDown", "mouseUp"
|
|
4772
|
-
description: 'Mouse action
|
|
6356
|
+
enum: ["click", "mouseDown", "mouseUp"],
|
|
6357
|
+
description: 'Mouse action. "click" does mouseDown + short delay + mouseUp.'
|
|
4773
6358
|
},
|
|
4774
6359
|
x: {
|
|
4775
6360
|
type: "number",
|
|
4776
|
-
description: "Viewport pixel X coordinate"
|
|
6361
|
+
description: "Viewport pixel X coordinate (as seen in capture_screenshot)"
|
|
4777
6362
|
},
|
|
4778
6363
|
y: {
|
|
4779
6364
|
type: "number",
|
|
4780
|
-
description: "Viewport pixel Y coordinate"
|
|
6365
|
+
description: "Viewport pixel Y coordinate (as seen in capture_screenshot)"
|
|
4781
6366
|
},
|
|
4782
6367
|
button: {
|
|
4783
6368
|
type: "string",
|
|
4784
6369
|
enum: ["Left", "Right", "Middle"],
|
|
4785
6370
|
description: "Mouse button (default: Left)"
|
|
4786
6371
|
},
|
|
4787
|
-
|
|
6372
|
+
target: {
|
|
4788
6373
|
type: "string",
|
|
4789
|
-
|
|
4790
|
-
description: 'Scroll direction (only for "scroll" action)'
|
|
6374
|
+
description: 'Instance target. Defaults to the running playtest client (client-1) when present, else "edit". Override with "server", "client-2", etc.'
|
|
4791
6375
|
},
|
|
4792
|
-
|
|
6376
|
+
instance_id: {
|
|
4793
6377
|
type: "string",
|
|
4794
|
-
description:
|
|
6378
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4795
6379
|
}
|
|
4796
6380
|
},
|
|
4797
6381
|
required: ["action", "x", "y"]
|
|
@@ -4800,13 +6384,13 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4800
6384
|
{
|
|
4801
6385
|
name: "simulate_keyboard_input",
|
|
4802
6386
|
category: "write",
|
|
4803
|
-
description: 'Simulate keyboard input via
|
|
6387
|
+
description: 'Simulate keyboard input in the running game via UserInputService:CreateVirtualInput. Use during a playtest for character movement (W/A/S/D walks at full WalkSpeed with player controls intact), jumping (Space), interactions (E), or any key-driven action. Drives the real input pipeline so game scripts and control modules respond. For sustained movement use action="press" to hold and "release" to let go. Pass "text" instead of keyCode to type a string into the focused TextBox. Auto-targets the running client; only works during a playtest.',
|
|
4804
6388
|
inputSchema: {
|
|
4805
6389
|
type: "object",
|
|
4806
6390
|
properties: {
|
|
4807
6391
|
keyCode: {
|
|
4808
6392
|
type: "string",
|
|
4809
|
-
description: 'Enum.KeyCode name: "W", "A", "S", "D", "Space", "E", "F", "LeftShift", "LeftControl", "Return", "Tab", "Escape", "One", "Two", etc.'
|
|
6393
|
+
description: 'Enum.KeyCode name: "W", "A", "S", "D", "Space", "E", "F", "LeftShift", "LeftControl", "Return", "Tab", "Escape", "One", "Two", etc. Omit if using "text".'
|
|
4810
6394
|
},
|
|
4811
6395
|
action: {
|
|
4812
6396
|
type: "string",
|
|
@@ -4817,12 +6401,19 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4817
6401
|
type: "number",
|
|
4818
6402
|
description: 'Hold duration in seconds for "tap" action (default: 0.1). Use longer values for sustained input like walking.'
|
|
4819
6403
|
},
|
|
6404
|
+
text: {
|
|
6405
|
+
type: "string",
|
|
6406
|
+
description: "Type this string into the currently focused TextBox (uses SendTextInput). When provided, keyCode/action are ignored."
|
|
6407
|
+
},
|
|
4820
6408
|
target: {
|
|
4821
6409
|
type: "string",
|
|
4822
|
-
description: 'Instance target
|
|
6410
|
+
description: 'Instance target. Defaults to the running playtest client (client-1) when present, else "edit". Override with "server", "client-2", etc.'
|
|
6411
|
+
},
|
|
6412
|
+
instance_id: {
|
|
6413
|
+
type: "string",
|
|
6414
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4823
6415
|
}
|
|
4824
|
-
}
|
|
4825
|
-
required: ["keyCode"]
|
|
6416
|
+
}
|
|
4826
6417
|
}
|
|
4827
6418
|
},
|
|
4828
6419
|
// === Character Navigation ===
|
|
@@ -4853,6 +6444,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4853
6444
|
target: {
|
|
4854
6445
|
type: "string",
|
|
4855
6446
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
6447
|
+
},
|
|
6448
|
+
instance_id: {
|
|
6449
|
+
type: "string",
|
|
6450
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4856
6451
|
}
|
|
4857
6452
|
}
|
|
4858
6453
|
}
|
|
@@ -4872,6 +6467,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4872
6467
|
targetParentPath: {
|
|
4873
6468
|
type: "string",
|
|
4874
6469
|
description: "Path of the parent to place the clone under"
|
|
6470
|
+
},
|
|
6471
|
+
instance_id: {
|
|
6472
|
+
type: "string",
|
|
6473
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4875
6474
|
}
|
|
4876
6475
|
},
|
|
4877
6476
|
required: ["instancePath", "targetParentPath"]
|
|
@@ -4896,6 +6495,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4896
6495
|
classFilter: {
|
|
4897
6496
|
type: "string",
|
|
4898
6497
|
description: 'Only include instances of this class (uses IsA, so "BasePart" matches Part, MeshPart, etc.)'
|
|
6498
|
+
},
|
|
6499
|
+
instance_id: {
|
|
6500
|
+
type: "string",
|
|
6501
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4899
6502
|
}
|
|
4900
6503
|
},
|
|
4901
6504
|
required: ["instancePath"]
|
|
@@ -4915,6 +6518,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4915
6518
|
instancePathB: {
|
|
4916
6519
|
type: "string",
|
|
4917
6520
|
description: "Second instance path"
|
|
6521
|
+
},
|
|
6522
|
+
instance_id: {
|
|
6523
|
+
type: "string",
|
|
6524
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4918
6525
|
}
|
|
4919
6526
|
},
|
|
4920
6527
|
required: ["instancePathA", "instancePathB"]
|
|
@@ -4935,6 +6542,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4935
6542
|
messageType: {
|
|
4936
6543
|
type: "string",
|
|
4937
6544
|
description: 'Filter by message type (e.g. "Enum.MessageType.MessageOutput", "Enum.MessageType.MessageWarning", "Enum.MessageType.MessageError")'
|
|
6545
|
+
},
|
|
6546
|
+
instance_id: {
|
|
6547
|
+
type: "string",
|
|
6548
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4938
6549
|
}
|
|
4939
6550
|
}
|
|
4940
6551
|
}
|
|
@@ -4954,6 +6565,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4954
6565
|
attributes: {
|
|
4955
6566
|
type: "object",
|
|
4956
6567
|
description: "Map of attribute names to values. Supports Vector3, Color3, UDim2 via _type convention."
|
|
6568
|
+
},
|
|
6569
|
+
instance_id: {
|
|
6570
|
+
type: "string",
|
|
6571
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4957
6572
|
}
|
|
4958
6573
|
},
|
|
4959
6574
|
required: ["instancePath", "attributes"]
|
|
@@ -4975,6 +6590,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4975
6590
|
type: "array",
|
|
4976
6591
|
items: { type: "string" },
|
|
4977
6592
|
description: "Optional DeveloperMemoryTag whitelist. Unknown tag names return 0 + unknown_tags list."
|
|
6593
|
+
},
|
|
6594
|
+
instance_id: {
|
|
6595
|
+
type: "string",
|
|
6596
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4978
6597
|
}
|
|
4979
6598
|
}
|
|
4980
6599
|
}
|
|
@@ -5000,6 +6619,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5000
6619
|
type: "string",
|
|
5001
6620
|
enum: ["edit", "server"],
|
|
5002
6621
|
description: 'Which DataModel to read from (default: "edit"). "server" serializes live runtime state during a playtest.'
|
|
6622
|
+
},
|
|
6623
|
+
instance_id: {
|
|
6624
|
+
type: "string",
|
|
6625
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5003
6626
|
}
|
|
5004
6627
|
},
|
|
5005
6628
|
required: ["instance_paths", "output_path"]
|
|
@@ -5034,6 +6657,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5034
6657
|
type: "string",
|
|
5035
6658
|
enum: ["edit", "server"],
|
|
5036
6659
|
description: 'Which DataModel to import into (default: "edit"). "server" parents into the live play-server DM.'
|
|
6660
|
+
},
|
|
6661
|
+
instance_id: {
|
|
6662
|
+
type: "string",
|
|
6663
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5037
6664
|
}
|
|
5038
6665
|
},
|
|
5039
6666
|
required: ["source", "parent_path"]
|
|
@@ -5079,6 +6706,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5079
6706
|
maxReplacements: {
|
|
5080
6707
|
type: "number",
|
|
5081
6708
|
description: "Safety limit on total replacements (default: 1000)"
|
|
6709
|
+
},
|
|
6710
|
+
instance_id: {
|
|
6711
|
+
type: "string",
|
|
6712
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5082
6713
|
}
|
|
5083
6714
|
},
|
|
5084
6715
|
required: ["pattern", "replacement"]
|