@el4cteo/rbx-studio-mcp 0.6.1 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -2
- package/dist/bridge/console.js +182 -0
- package/dist/bridge/console.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/cloudassets.js +233 -0
- package/dist/lib/cloudassets.js.map +1 -0
- package/dist/lib/credentials.js +180 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/livedata.js +325 -0
- package/dist/lib/livedata.js.map +1 -0
- package/dist/lib/liveluau.js +83 -0
- package/dist/lib/liveluau.js.map +1 -0
- package/dist/lib/liveops.js +358 -0
- package/dist/lib/liveops.js.map +1 -0
- package/dist/lib/opencloud.js +235 -0
- package/dist/lib/opencloud.js.map +1 -0
- package/dist/tools/anim.js +159 -0
- package/dist/tools/anim.js.map +1 -0
- package/dist/tools/audio.js +96 -0
- package/dist/tools/audio.js.map +1 -0
- package/dist/tools/character.js +95 -5
- package/dist/tools/character.js.map +1 -1
- package/dist/tools/data.js +292 -0
- package/dist/tools/data.js.map +1 -0
- package/dist/tools/device.js +77 -7
- package/dist/tools/device.js.map +1 -1
- package/dist/tools/discover.js +80 -4
- package/dist/tools/discover.js.map +1 -1
- package/dist/tools/exec.js +96 -2
- package/dist/tools/exec.js.map +1 -1
- package/dist/tools/input.js +35 -9
- package/dist/tools/input.js.map +1 -1
- package/dist/tools/perf.js +74 -7
- package/dist/tools/perf.js.map +1 -1
- package/dist/tools/scripts.js +162 -6
- package/dist/tools/scripts.js.map +1 -1
- package/dist/tools/spatial.js +135 -0
- package/dist/tools/spatial.js.map +1 -0
- package/dist/tools/universe.js +177 -0
- package/dist/tools/universe.js.map +1 -0
- package/dist/tools/upload.js +294 -0
- package/dist/tools/upload.js.map +1 -0
- package/dist/tools/world.js +675 -51
- package/dist/tools/world.js.map +1 -1
- package/package.json +2 -2
- package/plugin/src/Commands.luau +31 -7
- package/plugin/src/Config.luau +65 -65
- package/plugin/src/Console.luau +1909 -1843
- package/plugin/src/Emulation.luau +172 -0
- package/plugin/src/Phrase.luau +816 -618
- package/plugin/src/Png.luau +8 -4
- package/plugin/src/Prompt.luau +965 -961
- package/plugin/src/Secret.luau +86 -0
- package/plugin/src/Serialize.luau +440 -8
- package/plugin/src/Undo.luau +94 -6
- package/plugin/src/handlers/Anim.luau +897 -0
- package/plugin/src/handlers/Assets.luau +286 -2
- package/plugin/src/handlers/Audio.luau +411 -0
- package/plugin/src/handlers/Capture.luau +155 -20
- package/plugin/src/handlers/Character.luau +823 -361
- package/plugin/src/handlers/Data.luau +539 -0
- package/plugin/src/handlers/Device.luau +394 -139
- package/plugin/src/handlers/Discover.luau +685 -363
- package/plugin/src/handlers/Geometry.luau +722 -450
- package/plugin/src/handlers/Instances.luau +84 -4
- package/plugin/src/handlers/Perf.luau +227 -0
- package/plugin/src/handlers/Scripts.luau +673 -539
- package/plugin/src/handlers/Session.luau +3 -0
- package/plugin/src/handlers/Spatial.luau +334 -0
- package/plugin/src/handlers/Viewport.luau +268 -0
- package/plugin/src/handlers/World.luau +89 -15
- package/plugin/src/init.server.luau +9 -1
- package/scripts/build-plugin.mjs +20 -0
- package/scripts/check-plugin.mjs +171 -124
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { basename, extname, resolve } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { credentialStatus, loadCredentials } from "../lib/credentials.js";
|
|
5
|
+
import { json, text } from "../lib/format.js";
|
|
6
|
+
import { defineTool } from "../lib/tool.js";
|
|
7
|
+
/**
|
|
8
|
+
* Uploading goes out over Open Cloud rather than through the plugin.
|
|
9
|
+
*
|
|
10
|
+
* Studio's HttpService is not a route to this: a plugin cannot reach
|
|
11
|
+
* apis.roblox.com with a creator API key without shipping that key into the
|
|
12
|
+
* place, and the upload endpoint is multipart, which HttpService cannot build.
|
|
13
|
+
* The server is an ordinary Node process with the file already on disk, so it
|
|
14
|
+
* does the upload itself and hands Studio nothing but the finished asset id.
|
|
15
|
+
*/
|
|
16
|
+
const ASSETS_ENDPOINT = "https://apis.roblox.com/assets/v1/assets";
|
|
17
|
+
const OPERATIONS_ENDPOINT = "https://apis.roblox.com/assets/v1/operations";
|
|
18
|
+
/** Open Cloud's own cap, per call. */
|
|
19
|
+
const MAX_BYTES = 20 * 1024 * 1024;
|
|
20
|
+
/**
|
|
21
|
+
* Moderation runs before an asset is usable and it is not instant. Roblox
|
|
22
|
+
* returns an operation to poll rather than the asset, and audio in particular
|
|
23
|
+
* sits in review for a few seconds. Give up well before an agent's own patience
|
|
24
|
+
* runs out, and say what happened rather than reporting a failure.
|
|
25
|
+
*/
|
|
26
|
+
const POLL_TIMEOUT_MS = 90_000;
|
|
27
|
+
const POLL_INTERVAL_MS = 2_000;
|
|
28
|
+
/**
|
|
29
|
+
* Extension to (assetType, content type).
|
|
30
|
+
*
|
|
31
|
+
* Roblox validates both, and rejects mismatches with a message that names
|
|
32
|
+
* neither. Derived from the file rather than asked for, because an agent
|
|
33
|
+
* choosing "Model" for a .png is a failure that costs a round trip and a
|
|
34
|
+
* confusing error.
|
|
35
|
+
*/
|
|
36
|
+
const KINDS = {
|
|
37
|
+
".mp3": { assetType: "Audio", contentType: "audio/mpeg" },
|
|
38
|
+
".ogg": { assetType: "Audio", contentType: "audio/ogg" },
|
|
39
|
+
".wav": { assetType: "Audio", contentType: "audio/wav" },
|
|
40
|
+
".flac": { assetType: "Audio", contentType: "audio/flac" },
|
|
41
|
+
".png": { assetType: "Decal", contentType: "image/png" },
|
|
42
|
+
".jpg": { assetType: "Decal", contentType: "image/jpeg" },
|
|
43
|
+
".jpeg": { assetType: "Decal", contentType: "image/jpeg" },
|
|
44
|
+
".bmp": { assetType: "Decal", contentType: "image/bmp" },
|
|
45
|
+
".tga": { assetType: "Decal", contentType: "image/tga" },
|
|
46
|
+
".fbx": { assetType: "Model", contentType: "model/fbx" },
|
|
47
|
+
".gltf": { assetType: "Model", contentType: "model/gltf+json" },
|
|
48
|
+
".glb": { assetType: "Model", contentType: "model/gltf-binary" },
|
|
49
|
+
".mp4": { assetType: "Video", contentType: "video/mp4" },
|
|
50
|
+
".mov": { assetType: "Video", contentType: "video/mov" },
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The credentials, or a sentence explaining what is missing.
|
|
54
|
+
*
|
|
55
|
+
* Never taken as a tool argument. A key passed as one would be written into the
|
|
56
|
+
* conversation, sent to the model, and logged by the client — and it is exactly
|
|
57
|
+
* the credential that can publish under the user's name. It comes from the
|
|
58
|
+
* environment or from the panel's `cloud key` command instead, and this file
|
|
59
|
+
* never returns it to a caller.
|
|
60
|
+
*/
|
|
61
|
+
async function credentials() {
|
|
62
|
+
const found = await loadCredentials();
|
|
63
|
+
if (found !== null)
|
|
64
|
+
return found;
|
|
65
|
+
const status = await credentialStatus();
|
|
66
|
+
if (status.hasKey) {
|
|
67
|
+
return ("An Open Cloud key is set but there is no creator, so Roblox does not " +
|
|
68
|
+
"know who the asset belongs to.\n\n" +
|
|
69
|
+
"Type `cloud user <id>` in the Studio panel — the id is the number in " +
|
|
70
|
+
"your roblox.com/users/NNN/profile URL — or `cloud group <id>` to upload " +
|
|
71
|
+
"as a group. Setting ROBLOX_USER_ID in the environment works too.");
|
|
72
|
+
}
|
|
73
|
+
return ("No Open Cloud key is set, so nothing can be uploaded.\n\n" +
|
|
74
|
+
"The quickest way is the Studio panel: type `cloud` there and it walks " +
|
|
75
|
+
"through it. The line you type is masked in the log and in the history, and " +
|
|
76
|
+
"the key is stored in a file only you can read — it never reaches this " +
|
|
77
|
+
"conversation.\n\n" +
|
|
78
|
+
"It has to be done by the user, not by an agent, because it needs a " +
|
|
79
|
+
"logged-in browser:\n" +
|
|
80
|
+
"1. create.roblox.com/dashboard/credentials -> Create API Key\n" +
|
|
81
|
+
"2. Access Permissions -> add `assets`, with Read and Write\n" +
|
|
82
|
+
"3. Add your IP, save, copy the key (it is shown once)\n" +
|
|
83
|
+
"4. In the Studio panel: `cloud key <paste>` then `cloud user <your id>`\n\n" +
|
|
84
|
+
"Treat the key like a password: it can publish assets under that account.");
|
|
85
|
+
}
|
|
86
|
+
/** Turns Open Cloud's failure shapes into one sentence worth reading. */
|
|
87
|
+
function describeFailure(status, body) {
|
|
88
|
+
let detail = body.slice(0, 400);
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(body);
|
|
91
|
+
detail = parsed.message ?? parsed.errors?.[0]?.message ?? detail;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Not JSON — Roblox returns bare HTML for some gateway errors.
|
|
95
|
+
}
|
|
96
|
+
if (status === 401 || status === 403) {
|
|
97
|
+
return (`Roblox rejected the API key (${status}): ${detail}\n\n` +
|
|
98
|
+
"Check that the key has the `assets` permission with Write, that the " +
|
|
99
|
+
"creator id matches who the key was made for, and that your current IP " +
|
|
100
|
+
"is in the key's allowed list — an IP that has changed since the key was " +
|
|
101
|
+
"made is the most common cause of a 403.");
|
|
102
|
+
}
|
|
103
|
+
if (status === 429) {
|
|
104
|
+
return `Rate limited by Roblox: ${detail}\n\nWait and retry.`;
|
|
105
|
+
}
|
|
106
|
+
if (status === 400) {
|
|
107
|
+
return (`Roblox refused the file (400): ${detail}\n\n` +
|
|
108
|
+
"Usually the file does not match its extension, or it breaks a limit: " +
|
|
109
|
+
"audio over 7 minutes, images over 8000x8000.");
|
|
110
|
+
}
|
|
111
|
+
return `Roblox returned ${status}: ${detail}`;
|
|
112
|
+
}
|
|
113
|
+
async function poll(operationPath, apiKey) {
|
|
114
|
+
const id = operationPath.replace(/^operations\//, "");
|
|
115
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
116
|
+
for (;;) {
|
|
117
|
+
const response = await fetch(`${OPERATIONS_ENDPOINT}/${id}`, {
|
|
118
|
+
headers: { "x-api-key": apiKey },
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(describeFailure(response.status, await response.text()));
|
|
122
|
+
}
|
|
123
|
+
const operation = (await response.json());
|
|
124
|
+
if (operation.done)
|
|
125
|
+
return operation;
|
|
126
|
+
if (Date.now() >= deadline) {
|
|
127
|
+
throw new Error(`The upload is still processing after ${POLL_TIMEOUT_MS / 1000}s. It has not ` +
|
|
128
|
+
"failed — moderation can take longer. Check the Development Items tab " +
|
|
129
|
+
"on the Creator Dashboard in a minute.");
|
|
130
|
+
}
|
|
131
|
+
await new Promise((done) => setTimeout(done, POLL_INTERVAL_MS));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function registerUploadTools(context) {
|
|
135
|
+
const { bridge } = context;
|
|
136
|
+
defineTool(context, {
|
|
137
|
+
name: "upload",
|
|
138
|
+
title: "Upload a file to Roblox as an asset",
|
|
139
|
+
description: "Uploads a local file — audio, an image, a 3D model or a video — to " +
|
|
140
|
+
"Roblox and returns the asset id, so it can be used immediately.\n\n" +
|
|
141
|
+
"This closes the one hole nothing else here covers. Every other tool " +
|
|
142
|
+
"works with assets that already exist: `assets op=\"search\"` finds " +
|
|
143
|
+
"published ones, `generate` makes geometry from a prompt. A sound " +
|
|
144
|
+
"effect or a texture sitting in a folder on disk had to be dragged " +
|
|
145
|
+
"through Studio's import dialog by hand before anything could reference " +
|
|
146
|
+
"it. Now it does not.\n\n" +
|
|
147
|
+
"The asset type is worked out from the extension — .mp3/.ogg/.wav/.flac " +
|
|
148
|
+
"become Audio, .png/.jpg/.bmp/.tga become a Decal, .fbx/.gltf/.glb " +
|
|
149
|
+
"become a Model, .mp4/.mov become a Video.\n\n" +
|
|
150
|
+
"Set `insert` with a parent path to drop the result straight into the " +
|
|
151
|
+
"place once it is approved: a Decal upload becomes a Decal, a Model " +
|
|
152
|
+
"becomes the model. Audio is NOT inserted — an audio id belongs in an " +
|
|
153
|
+
"`AudioPlayer` or a `Sound`, so wire it up with `audio op=\"graph\"` " +
|
|
154
|
+
"and the id this returns.\n\n" +
|
|
155
|
+
"Uploads are moderated and the result says the verdict. They also count " +
|
|
156
|
+
"against a real quota: 10 audio uploads a month, or 100 if the account " +
|
|
157
|
+
"is ID-verified. Do not upload speculatively, and do not re-upload a " +
|
|
158
|
+
"file to retry — the first one probably worked.\n\n" +
|
|
159
|
+
"Needs a Roblox Open Cloud API key. The user sets it once by typing " +
|
|
160
|
+
"`cloud` in the Studio panel; never ask them to paste a key into this " +
|
|
161
|
+
"conversation. Call this tool with no arguments to check whether it is " +
|
|
162
|
+
"already set up.",
|
|
163
|
+
destructive: false,
|
|
164
|
+
inputSchema: {
|
|
165
|
+
file: z
|
|
166
|
+
.string()
|
|
167
|
+
.optional()
|
|
168
|
+
.describe("Path to the file on disk. Omit to check whether the credentials " +
|
|
169
|
+
"are set up without uploading anything."),
|
|
170
|
+
name: z
|
|
171
|
+
.string()
|
|
172
|
+
.optional()
|
|
173
|
+
.describe("Display name for the asset. Defaults to the file name. This is " +
|
|
174
|
+
"public and moderated — a name that breaks the rules fails the " +
|
|
175
|
+
"whole upload."),
|
|
176
|
+
description: z.string().optional().describe("Public description. Also moderated."),
|
|
177
|
+
assetType: z
|
|
178
|
+
.enum(["Audio", "Decal", "Model", "Video"])
|
|
179
|
+
.optional()
|
|
180
|
+
.describe("Override the type derived from the extension. Rarely right — " +
|
|
181
|
+
"Roblox validates the type against the file's real content."),
|
|
182
|
+
insert: z
|
|
183
|
+
.string()
|
|
184
|
+
.optional()
|
|
185
|
+
.describe('Where to put the asset in the place once approved, e.g. "Workspace". ' +
|
|
186
|
+
"Applies to Decal and Model uploads. Omit to only get the id back."),
|
|
187
|
+
studioId: z.string().optional().describe("Target Studio; omit for the active one."),
|
|
188
|
+
},
|
|
189
|
+
}, async (args) => {
|
|
190
|
+
const credential = await credentials();
|
|
191
|
+
if (typeof credential === "string")
|
|
192
|
+
return text(credential);
|
|
193
|
+
if (!args.file) {
|
|
194
|
+
return text(`Ready to upload: key set, uploading as ${credential.creatorField} ` +
|
|
195
|
+
`${credential.creatorId}. Call again with \`file\` pointing at the ` +
|
|
196
|
+
"file you want uploaded.");
|
|
197
|
+
}
|
|
198
|
+
const path = resolve(args.file);
|
|
199
|
+
let size;
|
|
200
|
+
try {
|
|
201
|
+
size = (await stat(path)).size;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return text(`There is no file at ${path}. Give an absolute path, or one relative ` +
|
|
205
|
+
"to the directory the server was started in.");
|
|
206
|
+
}
|
|
207
|
+
if (size === 0)
|
|
208
|
+
return text(`${path} is empty.`);
|
|
209
|
+
if (size > MAX_BYTES) {
|
|
210
|
+
return text(`${path} is ${(size / 1024 / 1024).toFixed(1)}MB. Open Cloud accepts ` +
|
|
211
|
+
"20MB per upload.");
|
|
212
|
+
}
|
|
213
|
+
const extension = extname(path).toLowerCase();
|
|
214
|
+
const kind = KINDS[extension];
|
|
215
|
+
if (!kind) {
|
|
216
|
+
return text(`Roblox does not take ${extension || "files with no extension"} through ` +
|
|
217
|
+
`Open Cloud. It takes: ${Object.keys(KINDS).join(", ")}.`);
|
|
218
|
+
}
|
|
219
|
+
const assetType = args.assetType ?? kind.assetType;
|
|
220
|
+
const form = new FormData();
|
|
221
|
+
form.append("request", JSON.stringify({
|
|
222
|
+
assetType,
|
|
223
|
+
displayName: args.name ?? basename(path, extension),
|
|
224
|
+
description: args.description ?? "",
|
|
225
|
+
creationContext: {
|
|
226
|
+
creator: { [credential.creatorField]: credential.creatorId },
|
|
227
|
+
},
|
|
228
|
+
}));
|
|
229
|
+
form.append("fileContent", new Blob([new Uint8Array(await readFile(path))], { type: kind.contentType }), basename(path));
|
|
230
|
+
const started = await fetch(ASSETS_ENDPOINT, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: { "x-api-key": credential.apiKey },
|
|
233
|
+
body: form,
|
|
234
|
+
});
|
|
235
|
+
if (!started.ok) {
|
|
236
|
+
return text(describeFailure(started.status, await started.text()));
|
|
237
|
+
}
|
|
238
|
+
const operation = (await started.json());
|
|
239
|
+
if (!operation.path) {
|
|
240
|
+
return text("Roblox accepted the upload but did not say where to track it. " +
|
|
241
|
+
"Check the Development Items tab on the Creator Dashboard.");
|
|
242
|
+
}
|
|
243
|
+
const finished = await poll(operation.path, credential.apiKey);
|
|
244
|
+
if (finished.error || !finished.response?.assetId) {
|
|
245
|
+
return text(`The upload failed moderation or validation: ` +
|
|
246
|
+
`${finished.error?.message ?? finished.message ?? "no reason given"}`);
|
|
247
|
+
}
|
|
248
|
+
const assetId = finished.response.assetId;
|
|
249
|
+
const moderation = finished.response.moderationResult?.moderationState ?? "unknown";
|
|
250
|
+
const result = {
|
|
251
|
+
assetId,
|
|
252
|
+
assetType,
|
|
253
|
+
uri: `rbxassetid://${assetId}`,
|
|
254
|
+
name: finished.response.displayName,
|
|
255
|
+
moderation,
|
|
256
|
+
uploadedAs: `${credential.creatorField} ${credential.creatorId}`,
|
|
257
|
+
file: path,
|
|
258
|
+
};
|
|
259
|
+
// Only inserted once Roblox says it is approved. Inserting a rejected
|
|
260
|
+
// asset puts an instance in the place that resolves to nothing, which
|
|
261
|
+
// reads as a bug in this tool rather than as a moderation decision.
|
|
262
|
+
if (args.insert && moderation === "MODERATION_STATE_APPROVED") {
|
|
263
|
+
if (assetType === "Audio") {
|
|
264
|
+
result["inserted"] = false;
|
|
265
|
+
result["note"] =
|
|
266
|
+
"Audio is not inserted on its own — the id goes into an AudioPlayer " +
|
|
267
|
+
'or a Sound. Build one with `audio op="graph" asset="' +
|
|
268
|
+
`rbxassetid://${assetId}"\`.`;
|
|
269
|
+
}
|
|
270
|
+
else if (assetType === "Decal") {
|
|
271
|
+
const created = await bridge.call("instances.create", {
|
|
272
|
+
instances: [
|
|
273
|
+
{
|
|
274
|
+
className: "Decal",
|
|
275
|
+
name: args.name ?? basename(path, extension),
|
|
276
|
+
parent: args.insert,
|
|
277
|
+
properties: { Texture: { value: `rbxassetid://${assetId}`, type: "ContentId" } },
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
}, { studioId: args.studioId, timeoutMs: 30_000 });
|
|
281
|
+
result["inserted"] = created;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
const inserted = await bridge.call("assets.insert", { assetId: Number(assetId), parent: args.insert }, { studioId: args.studioId, timeoutMs: 90_000 });
|
|
285
|
+
result["inserted"] = inserted;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return json(result, moderation === "MODERATION_STATE_APPROVED"
|
|
289
|
+
? `Use it as rbxassetid://${assetId}.`
|
|
290
|
+
: `Moderation says ${moderation}. The id exists but may not load until ` +
|
|
291
|
+
"review finishes.");
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/tools/upload.ts"],"names":[],"mappings":"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"}
|