dominus-cli 0.5.8 → 0.6.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 +244 -62
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +214 -54
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -252,7 +252,12 @@ var init_ws_server = __esm({
|
|
|
252
252
|
init_protocol();
|
|
253
253
|
DominusServer = class extends EventEmitter {
|
|
254
254
|
wss = null;
|
|
255
|
-
|
|
255
|
+
/** All connected Studio instances, keyed by placeId (0 for unknown) */
|
|
256
|
+
studioClients = /* @__PURE__ */ new Map();
|
|
257
|
+
/** Reverse lookup: ws → placeId */
|
|
258
|
+
wsToPlaceId = /* @__PURE__ */ new Map();
|
|
259
|
+
/** Which placeId to target. 0 = auto-select if only one is connected. */
|
|
260
|
+
activePlaceId = 0;
|
|
256
261
|
controllers = /* @__PURE__ */ new Set();
|
|
257
262
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
258
263
|
relayRequests = /* @__PURE__ */ new Map();
|
|
@@ -301,7 +306,9 @@ var init_ws_server = __esm({
|
|
|
301
306
|
this.addController(ws);
|
|
302
307
|
ws.send(serializeMessage(createMessage("controller:welcome", {
|
|
303
308
|
studioConnected: this.isConnected(),
|
|
304
|
-
port: this.port
|
|
309
|
+
port: this.port,
|
|
310
|
+
connections: this.listConnections(),
|
|
311
|
+
activePlaceId: this.activePlaceId
|
|
305
312
|
})));
|
|
306
313
|
return;
|
|
307
314
|
}
|
|
@@ -309,7 +316,7 @@ var init_ws_server = __esm({
|
|
|
309
316
|
if (this.controllers.has(ws)) {
|
|
310
317
|
this.handleControllerMessage(ws, msg);
|
|
311
318
|
} else {
|
|
312
|
-
this.handleStudioMessage(msg);
|
|
319
|
+
this.handleStudioMessage(ws, msg);
|
|
313
320
|
}
|
|
314
321
|
} catch (err) {
|
|
315
322
|
this.emit("server:error", err);
|
|
@@ -321,14 +328,22 @@ var init_ws_server = __esm({
|
|
|
321
328
|
for (const [id, controllerWs] of this.relayRequests) {
|
|
322
329
|
if (controllerWs === ws) this.relayRequests.delete(id);
|
|
323
330
|
}
|
|
324
|
-
} else
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
this.
|
|
331
|
+
} else {
|
|
332
|
+
const placeId = this.wsToPlaceId.get(ws);
|
|
333
|
+
if (placeId !== void 0) {
|
|
334
|
+
const conn = this.studioClients.get(placeId);
|
|
335
|
+
this.studioClients.delete(placeId);
|
|
336
|
+
this.wsToPlaceId.delete(ws);
|
|
337
|
+
this.emit("studio:disconnected", { placeId, placeName: conn?.placeName });
|
|
338
|
+
this.broadcastToControllers("studio:disconnected", { placeId, placeName: conn?.placeName });
|
|
339
|
+
for (const [id, pending] of this.pendingRequests) {
|
|
340
|
+
clearTimeout(pending.timer);
|
|
341
|
+
pending.reject(new Error(`Studio disconnected (place ${placeId})`));
|
|
342
|
+
this.pendingRequests.delete(id);
|
|
343
|
+
}
|
|
344
|
+
if (this.activePlaceId === placeId) {
|
|
345
|
+
this.activePlaceId = 0;
|
|
346
|
+
}
|
|
332
347
|
}
|
|
333
348
|
}
|
|
334
349
|
});
|
|
@@ -337,19 +352,24 @@ var init_ws_server = __esm({
|
|
|
337
352
|
});
|
|
338
353
|
}
|
|
339
354
|
setStudioClient(ws, msg) {
|
|
340
|
-
if (this.studioClient) {
|
|
341
|
-
this.studioClient.ws.close();
|
|
342
|
-
}
|
|
343
355
|
const payload = msg.payload;
|
|
344
|
-
|
|
356
|
+
const placeId = payload.placeId ?? 0;
|
|
357
|
+
const existing = this.studioClients.get(placeId);
|
|
358
|
+
if (existing && existing.ws !== ws) {
|
|
359
|
+
existing.ws.close();
|
|
360
|
+
this.wsToPlaceId.delete(existing.ws);
|
|
361
|
+
}
|
|
362
|
+
const conn = {
|
|
345
363
|
ws,
|
|
346
364
|
connectedAt: Date.now(),
|
|
347
365
|
studioVersion: payload.studioVersion,
|
|
348
|
-
placeId
|
|
366
|
+
placeId,
|
|
349
367
|
placeName: payload.placeName
|
|
350
368
|
};
|
|
351
|
-
this.
|
|
352
|
-
this.
|
|
369
|
+
this.studioClients.set(placeId, conn);
|
|
370
|
+
this.wsToPlaceId.set(ws, placeId);
|
|
371
|
+
this.emit("studio:connected", { ...payload, placeId });
|
|
372
|
+
this.broadcastToControllers("studio:connected", { ...payload, placeId });
|
|
353
373
|
}
|
|
354
374
|
addController(ws) {
|
|
355
375
|
this.controllers.add(ws);
|
|
@@ -364,25 +384,30 @@ var init_ws_server = __esm({
|
|
|
364
384
|
}
|
|
365
385
|
}
|
|
366
386
|
handleControllerMessage(controllerWs, msg) {
|
|
367
|
-
|
|
387
|
+
const target = this.resolveTarget();
|
|
388
|
+
if (!target || target.ws.readyState !== WebSocket.OPEN) {
|
|
368
389
|
controllerWs.send(serializeMessage({
|
|
369
390
|
id: msg.id,
|
|
370
391
|
type: StudioMsg.RESPONSE,
|
|
371
|
-
payload: { error:
|
|
392
|
+
payload: { error: this.getTargetError() },
|
|
372
393
|
ts: Date.now()
|
|
373
394
|
}));
|
|
374
395
|
return;
|
|
375
396
|
}
|
|
376
397
|
this.relayRequests.set(msg.id, controllerWs);
|
|
377
|
-
|
|
398
|
+
target.ws.send(serializeMessage(msg));
|
|
378
399
|
}
|
|
379
|
-
handleStudioMessage(msg) {
|
|
400
|
+
handleStudioMessage(ws, msg) {
|
|
380
401
|
if (msg.type === StudioMsg.CONNECTED) {
|
|
381
402
|
const payload = msg.payload;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
403
|
+
const placeId = this.wsToPlaceId.get(ws);
|
|
404
|
+
if (placeId !== void 0) {
|
|
405
|
+
const conn = this.studioClients.get(placeId);
|
|
406
|
+
if (conn) {
|
|
407
|
+
conn.studioVersion = payload.studioVersion;
|
|
408
|
+
conn.placeId = payload.placeId;
|
|
409
|
+
conn.placeName = payload.placeName;
|
|
410
|
+
}
|
|
386
411
|
}
|
|
387
412
|
this.emit("studio:connected", payload);
|
|
388
413
|
this.broadcastToControllers("studio:connected", payload);
|
|
@@ -407,10 +432,35 @@ var init_ws_server = __esm({
|
|
|
407
432
|
this.emit("message", msg);
|
|
408
433
|
this.broadcastToControllers(msg.type, msg.payload);
|
|
409
434
|
}
|
|
435
|
+
/** Resolve which Studio connection to target */
|
|
436
|
+
resolveTarget() {
|
|
437
|
+
if (this.studioClients.size === 0) return null;
|
|
438
|
+
if (this.activePlaceId !== 0) {
|
|
439
|
+
const conn = this.studioClients.get(this.activePlaceId);
|
|
440
|
+
if (conn && conn.ws.readyState === WebSocket.OPEN) return conn;
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
if (this.studioClients.size === 1) {
|
|
444
|
+
const conn = this.studioClients.values().next().value;
|
|
445
|
+
if (conn.ws.readyState === WebSocket.OPEN) return conn;
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
/** Generate an appropriate error message when no target found */
|
|
451
|
+
getTargetError() {
|
|
452
|
+
if (this.studioClients.size === 0) return "No Studio connection";
|
|
453
|
+
if (this.activePlaceId !== 0) {
|
|
454
|
+
return `Active place ${this.activePlaceId} is not connected. Use set_active_place or list_connections.`;
|
|
455
|
+
}
|
|
456
|
+
const places = [...this.studioClients.values()].map((c) => `${c.placeName ?? "Unknown"} (${c.placeId})`);
|
|
457
|
+
return `Multiple Studio instances connected: ${places.join(", ")}. Use set_active_place to choose one.`;
|
|
458
|
+
}
|
|
410
459
|
sendRequest(type, payload, timeoutMs = 1e4) {
|
|
411
460
|
return new Promise((resolve, reject) => {
|
|
412
|
-
|
|
413
|
-
|
|
461
|
+
const target = this.resolveTarget();
|
|
462
|
+
if (!target || target.ws.readyState !== WebSocket.OPEN) {
|
|
463
|
+
reject(new Error(this.getTargetError()));
|
|
414
464
|
return;
|
|
415
465
|
}
|
|
416
466
|
const msg = createMessage(type, payload);
|
|
@@ -423,31 +473,45 @@ var init_ws_server = __esm({
|
|
|
423
473
|
reject,
|
|
424
474
|
timer
|
|
425
475
|
});
|
|
426
|
-
|
|
476
|
+
target.ws.send(serializeMessage(msg));
|
|
427
477
|
});
|
|
428
478
|
}
|
|
429
479
|
sendNotification(type, payload) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
480
|
+
const target = this.resolveTarget();
|
|
481
|
+
if (!target || target.ws.readyState !== WebSocket.OPEN) return;
|
|
433
482
|
const msg = createMessage(type, payload);
|
|
434
|
-
|
|
483
|
+
target.ws.send(serializeMessage(msg));
|
|
435
484
|
}
|
|
436
485
|
isConnected() {
|
|
437
|
-
return this.
|
|
486
|
+
return this.resolveTarget() !== null;
|
|
438
487
|
}
|
|
439
488
|
getConnectionInfo() {
|
|
440
|
-
return this.
|
|
489
|
+
return this.resolveTarget();
|
|
490
|
+
}
|
|
491
|
+
listConnections() {
|
|
492
|
+
return [...this.studioClients.values()].filter((c) => c.ws.readyState === WebSocket.OPEN).map((c) => ({
|
|
493
|
+
placeId: c.placeId ?? 0,
|
|
494
|
+
placeName: c.placeName,
|
|
495
|
+
studioVersion: c.studioVersion,
|
|
496
|
+
connectedAt: c.connectedAt
|
|
497
|
+
}));
|
|
498
|
+
}
|
|
499
|
+
getActivePlaceId() {
|
|
500
|
+
return this.activePlaceId;
|
|
501
|
+
}
|
|
502
|
+
setActivePlaceId(placeId) {
|
|
503
|
+
this.activePlaceId = placeId;
|
|
441
504
|
}
|
|
442
505
|
getPort() {
|
|
443
506
|
return this.port;
|
|
444
507
|
}
|
|
445
508
|
stop() {
|
|
446
509
|
return new Promise((resolve) => {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
this.studioClient = null;
|
|
510
|
+
for (const conn of this.studioClients.values()) {
|
|
511
|
+
conn.ws.close();
|
|
450
512
|
}
|
|
513
|
+
this.studioClients.clear();
|
|
514
|
+
this.wsToPlaceId.clear();
|
|
451
515
|
for (const ws of this.controllers) {
|
|
452
516
|
ws.close();
|
|
453
517
|
}
|
|
@@ -494,7 +558,8 @@ var init_ws_client = __esm({
|
|
|
494
558
|
ws = null;
|
|
495
559
|
port;
|
|
496
560
|
studioConnected = false;
|
|
497
|
-
|
|
561
|
+
connections = /* @__PURE__ */ new Map();
|
|
562
|
+
activePlaceId = 0;
|
|
498
563
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
499
564
|
constructor(port = 18088) {
|
|
500
565
|
super();
|
|
@@ -538,6 +603,20 @@ var init_ws_client = __esm({
|
|
|
538
603
|
if (msg.type === "controller:welcome") {
|
|
539
604
|
const p = msg.payload;
|
|
540
605
|
this.studioConnected = p.studioConnected;
|
|
606
|
+
if (p.connections) {
|
|
607
|
+
this.connections.clear();
|
|
608
|
+
for (const c of p.connections) {
|
|
609
|
+
this.connections.set(c.placeId, {
|
|
610
|
+
connectedAt: c.connectedAt,
|
|
611
|
+
studioVersion: c.studioVersion,
|
|
612
|
+
placeId: c.placeId,
|
|
613
|
+
placeName: c.placeName
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (p.activePlaceId !== void 0) {
|
|
618
|
+
this.activePlaceId = p.activePlaceId;
|
|
619
|
+
}
|
|
541
620
|
if (this.studioConnected) {
|
|
542
621
|
this.emit("studio:connected", {});
|
|
543
622
|
}
|
|
@@ -546,19 +625,27 @@ var init_ws_client = __esm({
|
|
|
546
625
|
if (msg.type === "studio:connected") {
|
|
547
626
|
this.studioConnected = true;
|
|
548
627
|
const payload = msg.payload;
|
|
549
|
-
|
|
628
|
+
const placeId = payload.placeId ?? 0;
|
|
629
|
+
this.connections.set(placeId, {
|
|
550
630
|
connectedAt: Date.now(),
|
|
551
631
|
studioVersion: payload.studioVersion,
|
|
552
|
-
placeId
|
|
632
|
+
placeId,
|
|
553
633
|
placeName: payload.placeName
|
|
554
|
-
};
|
|
634
|
+
});
|
|
555
635
|
this.emit("studio:connected", payload);
|
|
556
636
|
return;
|
|
557
637
|
}
|
|
558
638
|
if (msg.type === "studio:disconnected") {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
this.
|
|
639
|
+
const payload = msg.payload;
|
|
640
|
+
const placeId = payload.placeId ?? 0;
|
|
641
|
+
this.connections.delete(placeId);
|
|
642
|
+
if (this.connections.size === 0) {
|
|
643
|
+
this.studioConnected = false;
|
|
644
|
+
}
|
|
645
|
+
if (this.activePlaceId === placeId) {
|
|
646
|
+
this.activePlaceId = 0;
|
|
647
|
+
}
|
|
648
|
+
this.emit("studio:disconnected", payload);
|
|
562
649
|
return;
|
|
563
650
|
}
|
|
564
651
|
if (msg.type === StudioMsg.RESPONSE) {
|
|
@@ -602,11 +689,27 @@ var init_ws_client = __esm({
|
|
|
602
689
|
this.ws.send(serializeMessage(msg));
|
|
603
690
|
}
|
|
604
691
|
isConnected() {
|
|
605
|
-
return this.studioConnected;
|
|
692
|
+
return this.studioConnected && this.connections.size > 0;
|
|
606
693
|
}
|
|
607
694
|
getConnectionInfo() {
|
|
608
|
-
if (
|
|
609
|
-
|
|
695
|
+
if (this.connections.size === 0) return null;
|
|
696
|
+
const target = this.activePlaceId !== 0 ? this.connections.get(this.activePlaceId) : this.connections.values().next().value;
|
|
697
|
+
if (!target) return null;
|
|
698
|
+
return { ...target, ws: this.ws };
|
|
699
|
+
}
|
|
700
|
+
listConnections() {
|
|
701
|
+
return [...this.connections.values()].map((c) => ({
|
|
702
|
+
placeId: c.placeId ?? 0,
|
|
703
|
+
placeName: c.placeName,
|
|
704
|
+
studioVersion: c.studioVersion,
|
|
705
|
+
connectedAt: c.connectedAt
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
getActivePlaceId() {
|
|
709
|
+
return this.activePlaceId;
|
|
710
|
+
}
|
|
711
|
+
setActivePlaceId(placeId) {
|
|
712
|
+
this.activePlaceId = placeId;
|
|
610
713
|
}
|
|
611
714
|
getPort() {
|
|
612
715
|
return this.port;
|
|
@@ -683,6 +786,7 @@ These are built-in lessons that ship with Dominus. Follow them exactly.
|
|
|
683
786
|
- **Inspecting user's selection \u2192 \`get_selection\`, then \`get_properties\` or \`serialize_ui\`**
|
|
684
787
|
- **Reading properties \u2192 \`get_properties\` (NEVER \`run_code\` with dump scripts)**
|
|
685
788
|
- **Browsing the tree \u2192 \`get_explorer\`**
|
|
789
|
+
- **Multiple Studios open \u2192 \`list_connections\` to see all, \`set_active_place\` to target one**
|
|
686
790
|
- Anything exotic or procedural \u2192 \`run_code\` as last resort
|
|
687
791
|
|
|
688
792
|
### NEVER use run_code for:
|
|
@@ -2605,7 +2709,7 @@ async function startMcpServer() {
|
|
|
2605
2709
|
}
|
|
2606
2710
|
const mcp = new McpServer({
|
|
2607
2711
|
name: "dominus",
|
|
2608
|
-
version: "0.
|
|
2712
|
+
version: "0.6.0"
|
|
2609
2713
|
}, {
|
|
2610
2714
|
instructions: INTERNAL_MEMORY
|
|
2611
2715
|
});
|
|
@@ -2618,6 +2722,11 @@ async function startMcpServer() {
|
|
|
2618
2722
|
for (const p of parts) r += `[${luaStr(p)}]`;
|
|
2619
2723
|
return r;
|
|
2620
2724
|
}
|
|
2725
|
+
function cleanNum(n) {
|
|
2726
|
+
if (Number.isInteger(n)) return String(n);
|
|
2727
|
+
const rounded = Math.round(n * 1e3) / 1e3;
|
|
2728
|
+
return String(rounded);
|
|
2729
|
+
}
|
|
2621
2730
|
function generateComponentCode(tree, componentName, framework) {
|
|
2622
2731
|
const isReact = framework === "react";
|
|
2623
2732
|
const lib = isReact ? "React" : "Roact";
|
|
@@ -2675,7 +2784,7 @@ async function startMcpServer() {
|
|
|
2675
2784
|
function luaValue(val, _depth) {
|
|
2676
2785
|
if (val === null || val === void 0) return "nil";
|
|
2677
2786
|
if (typeof val === "boolean") return String(val);
|
|
2678
|
-
if (typeof val === "number") return
|
|
2787
|
+
if (typeof val === "number") return cleanNum(val);
|
|
2679
2788
|
if (typeof val === "string") {
|
|
2680
2789
|
if (val.startsWith("#") && val.length === 7) {
|
|
2681
2790
|
return `Color3.fromHex("${val}")`;
|
|
@@ -2687,15 +2796,15 @@ async function startMcpServer() {
|
|
|
2687
2796
|
}
|
|
2688
2797
|
if (Array.isArray(val)) {
|
|
2689
2798
|
if (val.length === 4 && val.every((v) => typeof v === "number")) {
|
|
2690
|
-
return `UDim2.new(${val.join(", ")})`;
|
|
2799
|
+
return `UDim2.new(${val.map(cleanNum).join(", ")})`;
|
|
2691
2800
|
}
|
|
2692
2801
|
if (val.length === 2 && val.every((v) => typeof v === "number")) {
|
|
2693
|
-
return `Vector2.new(${val.join(", ")})`;
|
|
2802
|
+
return `Vector2.new(${val.map(cleanNum).join(", ")})`;
|
|
2694
2803
|
}
|
|
2695
2804
|
if (val.length === 3 && val.every((v) => typeof v === "number")) {
|
|
2696
2805
|
const allSmall = val.every((v) => v <= 1);
|
|
2697
|
-
if (allSmall) return `Color3.new(${val.join(", ")})`;
|
|
2698
|
-
return `Color3.fromRGB(${val.join(", ")})`;
|
|
2806
|
+
if (allSmall) return `Color3.new(${val.map(cleanNum).join(", ")})`;
|
|
2807
|
+
return `Color3.fromRGB(${val.map((v) => String(Math.round(v))).join(", ")})`;
|
|
2699
2808
|
}
|
|
2700
2809
|
return `{${val.map((v) => luaValue(v, _depth)).join(", ")}}`;
|
|
2701
2810
|
}
|
|
@@ -3188,6 +3297,57 @@ ${" ".repeat(_depth)}}`;
|
|
|
3188
3297
|
return { content: [{ type: "text", text: code }] };
|
|
3189
3298
|
}
|
|
3190
3299
|
);
|
|
3300
|
+
mcp.tool(
|
|
3301
|
+
"list_connections",
|
|
3302
|
+
"List all Roblox Studio instances currently connected to Dominus. Shows placeId, placeName, and connection time for each. Use this when working with multiple Studio windows.",
|
|
3303
|
+
{},
|
|
3304
|
+
async () => {
|
|
3305
|
+
const connections = bridge.listConnections();
|
|
3306
|
+
const activePlaceId = bridge.getActivePlaceId();
|
|
3307
|
+
return {
|
|
3308
|
+
content: [{
|
|
3309
|
+
type: "text",
|
|
3310
|
+
text: JSON.stringify({
|
|
3311
|
+
activePlaceId: activePlaceId || "auto",
|
|
3312
|
+
connections: connections.map((c) => ({
|
|
3313
|
+
...c,
|
|
3314
|
+
active: activePlaceId === 0 && connections.length === 1 ? true : c.placeId === activePlaceId
|
|
3315
|
+
}))
|
|
3316
|
+
}, null, 2)
|
|
3317
|
+
}]
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
);
|
|
3321
|
+
mcp.tool(
|
|
3322
|
+
"set_active_place",
|
|
3323
|
+
"Set which Roblox Studio instance to target when multiple are connected. Pass placeId=0 to reset to auto mode (works when only one is connected). Call list_connections first to see available placeIds.",
|
|
3324
|
+
{
|
|
3325
|
+
placeId: z.number().describe("The placeId to target. Use 0 to reset to auto-select.")
|
|
3326
|
+
},
|
|
3327
|
+
async ({ placeId }) => {
|
|
3328
|
+
const connections = bridge.listConnections();
|
|
3329
|
+
if (placeId !== 0 && !connections.some((c) => c.placeId === placeId)) {
|
|
3330
|
+
const available = connections.map((c) => `${c.placeName ?? "Unknown"} (${c.placeId})`).join(", ");
|
|
3331
|
+
return {
|
|
3332
|
+
content: [{
|
|
3333
|
+
type: "text",
|
|
3334
|
+
text: `Place ${placeId} is not connected. Available: ${available || "none"}`
|
|
3335
|
+
}]
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3338
|
+
bridge.setActivePlaceId(placeId);
|
|
3339
|
+
if (placeId === 0) {
|
|
3340
|
+
return { content: [{ type: "text", text: "Active place reset to auto-select." }] };
|
|
3341
|
+
}
|
|
3342
|
+
const target = connections.find((c) => c.placeId === placeId);
|
|
3343
|
+
return {
|
|
3344
|
+
content: [{
|
|
3345
|
+
type: "text",
|
|
3346
|
+
text: `Active place set to ${target?.placeName ?? "Unknown"} (${placeId})`
|
|
3347
|
+
}]
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
);
|
|
3191
3351
|
mcp.tool(
|
|
3192
3352
|
"recall_memory",
|
|
3193
3353
|
"Search persistent memory for facts about the current Roblox project",
|
|
@@ -3482,11 +3642,20 @@ var Setup = ({ onComplete }) => {
|
|
|
3482
3642
|
var StatusBar = React6.memo(({
|
|
3483
3643
|
connected,
|
|
3484
3644
|
placeName,
|
|
3645
|
+
connectionCount,
|
|
3485
3646
|
model,
|
|
3486
3647
|
sessionTokens
|
|
3487
3648
|
}) => {
|
|
3488
3649
|
const statusColor = connected ? "green" : "red";
|
|
3489
|
-
const
|
|
3650
|
+
const count = connectionCount ?? (connected ? 1 : 0);
|
|
3651
|
+
let statusText;
|
|
3652
|
+
if (!connected) {
|
|
3653
|
+
statusText = "\u25CB Disconnected";
|
|
3654
|
+
} else if (count > 1) {
|
|
3655
|
+
statusText = `\u25CF ${count} Studios${placeName ? ` \u2014 active: ${placeName}` : ""}`;
|
|
3656
|
+
} else {
|
|
3657
|
+
statusText = `\u25CF Connected${placeName ? ` \u2014 ${placeName}` : ""}`;
|
|
3658
|
+
}
|
|
3490
3659
|
const shortModel = model.split("/").pop() ?? model;
|
|
3491
3660
|
return /* @__PURE__ */ jsxs(
|
|
3492
3661
|
Box,
|
|
@@ -3904,6 +4073,7 @@ var ToolCallView = React6.memo(({ calls }) => {
|
|
|
3904
4073
|
var Layout = React6.memo(({
|
|
3905
4074
|
connected,
|
|
3906
4075
|
placeName,
|
|
4076
|
+
connectionCount,
|
|
3907
4077
|
model,
|
|
3908
4078
|
chatEntries,
|
|
3909
4079
|
outputEntries,
|
|
@@ -3917,7 +4087,7 @@ var Layout = React6.memo(({
|
|
|
3917
4087
|
onClearImage
|
|
3918
4088
|
}) => {
|
|
3919
4089
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", children: [
|
|
3920
|
-
/* @__PURE__ */ jsx(StatusBar, { connected, placeName, model }),
|
|
4090
|
+
/* @__PURE__ */ jsx(StatusBar, { connected, placeName, connectionCount, model }),
|
|
3921
4091
|
/* @__PURE__ */ jsxs(Box, { flexGrow: 1, children: [
|
|
3922
4092
|
/* @__PURE__ */ jsx(ExplorerPane, { tree: explorerTree }),
|
|
3923
4093
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
|
|
@@ -3943,10 +4113,12 @@ var Layout = React6.memo(({
|
|
|
3943
4113
|
init_protocol();
|
|
3944
4114
|
function useStudio(server) {
|
|
3945
4115
|
const connInfo = server.getConnectionInfo();
|
|
4116
|
+
const connections = server.listConnections();
|
|
3946
4117
|
const [state, setState] = useState({
|
|
3947
4118
|
connected: server.isConnected(),
|
|
3948
4119
|
placeName: connInfo?.placeName,
|
|
3949
4120
|
placeId: connInfo?.placeId,
|
|
4121
|
+
connectionCount: connections.length,
|
|
3950
4122
|
explorerTree: [],
|
|
3951
4123
|
outputLog: []
|
|
3952
4124
|
});
|
|
@@ -3955,27 +4127,36 @@ function useStudio(server) {
|
|
|
3955
4127
|
useEffect(() => {
|
|
3956
4128
|
if (server.isConnected() && !stateRef.current.connected) {
|
|
3957
4129
|
const info = server.getConnectionInfo();
|
|
4130
|
+
const conns = server.listConnections();
|
|
3958
4131
|
setState((prev) => ({
|
|
3959
4132
|
...prev,
|
|
3960
4133
|
connected: true,
|
|
3961
4134
|
placeName: info?.placeName,
|
|
3962
|
-
placeId: info?.placeId
|
|
4135
|
+
placeId: info?.placeId,
|
|
4136
|
+
connectionCount: conns.length
|
|
3963
4137
|
}));
|
|
3964
4138
|
}
|
|
3965
4139
|
const onConnected = (payload) => {
|
|
4140
|
+
const conns = server.listConnections();
|
|
4141
|
+
const info = server.getConnectionInfo();
|
|
3966
4142
|
setState((prev) => ({
|
|
3967
4143
|
...prev,
|
|
3968
4144
|
connected: true,
|
|
3969
|
-
placeName: payload.placeName ?? prev.placeName,
|
|
3970
|
-
placeId: payload.placeId ?? prev.placeId
|
|
4145
|
+
placeName: info?.placeName ?? payload.placeName ?? prev.placeName,
|
|
4146
|
+
placeId: info?.placeId ?? payload.placeId ?? prev.placeId,
|
|
4147
|
+
connectionCount: conns.length
|
|
3971
4148
|
}));
|
|
3972
4149
|
};
|
|
3973
4150
|
const onDisconnected = () => {
|
|
4151
|
+
const conns = server.listConnections();
|
|
4152
|
+
const info = server.getConnectionInfo();
|
|
4153
|
+
const stillConnected = conns.length > 0;
|
|
3974
4154
|
setState((prev) => ({
|
|
3975
4155
|
...prev,
|
|
3976
|
-
connected:
|
|
3977
|
-
placeName: void 0,
|
|
3978
|
-
placeId: void 0
|
|
4156
|
+
connected: stillConnected,
|
|
4157
|
+
placeName: stillConnected ? info?.placeName : void 0,
|
|
4158
|
+
placeId: stillConnected ? info?.placeId : void 0,
|
|
4159
|
+
connectionCount: conns.length
|
|
3979
4160
|
}));
|
|
3980
4161
|
};
|
|
3981
4162
|
const onExplorer = (payload) => {
|
|
@@ -4277,6 +4458,7 @@ var App = ({ server, agent: initialAgent, version }) => {
|
|
|
4277
4458
|
{
|
|
4278
4459
|
connected: studio.connected,
|
|
4279
4460
|
placeName: studio.placeName,
|
|
4461
|
+
connectionCount: studio.connectionCount,
|
|
4280
4462
|
model: agent?.getModel() ?? resolveProviderSettings(config).model,
|
|
4281
4463
|
chatEntries,
|
|
4282
4464
|
outputEntries: studio.outputLog,
|