@adiholick/nvstudio-mcp 2.0.3 → 2.0.4
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 +1 -1
- package/dist/roblox-bridge.js +19 -0
- package/package.json +1 -1
- package/studio-plugin/Main.server.lua +66 -43
- package/studio-plugin/nvstudio_mcp.rbxmx +66 -43
package/dist/index.js
CHANGED
package/dist/roblox-bridge.js
CHANGED
|
@@ -89,6 +89,25 @@ function startBridgeServer(port = 3055) {
|
|
|
89
89
|
app.get('/api/stats', (req, res) => {
|
|
90
90
|
res.json({ totalTasks, successCount, errorCount, sessionStart, studioConnected, port });
|
|
91
91
|
});
|
|
92
|
+
// Endpoint Studio Instant Ping (Handshake cepat tanpa menunggu long-polling)
|
|
93
|
+
app.get('/api/ping', (req, res) => {
|
|
94
|
+
if (!studioConnected) {
|
|
95
|
+
studioConnected = true;
|
|
96
|
+
io.emit('studio-status', { connected: true });
|
|
97
|
+
addLog(io, 'system', 'Roblox Studio terhubung ke Bridge Server via Ping.');
|
|
98
|
+
}
|
|
99
|
+
if (!dashboardOpened) {
|
|
100
|
+
dashboardOpened = true;
|
|
101
|
+
const url = `http://localhost:${port}`;
|
|
102
|
+
const startCmd = process.platform === 'win32' ? `start "" "${url}"` : process.platform === 'darwin' ? `open "${url}"` : `xdg-open "${url}"`;
|
|
103
|
+
(0, child_process_1.exec)(startCmd, (error) => {
|
|
104
|
+
if (error)
|
|
105
|
+
console.error(`[Bridge] Gagal membuka browser:`, error);
|
|
106
|
+
});
|
|
107
|
+
console.error(`[Bridge] Pertama kali terhubung dengan Studio! Membuka Dashboard...`);
|
|
108
|
+
}
|
|
109
|
+
res.json({ status: 'ok', studioConnected: true, timestamp: Date.now() });
|
|
110
|
+
});
|
|
92
111
|
const waitingPollers = [];
|
|
93
112
|
task_queue_1.taskEmitter.on('new_task', () => {
|
|
94
113
|
if (task_queue_1.taskQueue.length > 0 && waitingPollers.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
local HttpService = game:GetService("HttpService")
|
|
2
2
|
local LogService = game:GetService("LogService")
|
|
3
3
|
|
|
4
|
+
local PING_URL = "http://localhost:3055/api/ping"
|
|
4
5
|
local TASK_URL = "http://localhost:3055/api/tasks"
|
|
5
6
|
local RESPONSE_URL = "http://localhost:3055/api/response"
|
|
6
7
|
|
|
@@ -24,8 +25,10 @@ local ctx = {
|
|
|
24
25
|
}
|
|
25
26
|
local MAX_LOGS = 50
|
|
26
27
|
local isConnected = false
|
|
27
|
-
local
|
|
28
|
+
local savedAutoConnect = plugin:GetSetting("AutoConnect")
|
|
29
|
+
local autoConnect = (savedAutoConnect == nil) and true or savedAutoConnect
|
|
28
30
|
local isPolling = false
|
|
31
|
+
local isManualDisconnect = false -- Mencegah auto-reconnect saat user sengaja disconnect
|
|
29
32
|
local isClickDebounce = false -- Mencegah rapid double-click
|
|
30
33
|
|
|
31
34
|
-- Deklarasi fungsi polling di awal agar bisa dipanggil oleh UI
|
|
@@ -39,7 +42,11 @@ if uiModule then
|
|
|
39
42
|
|
|
40
43
|
-- Sync Initial State ke UI
|
|
41
44
|
ui:setAutoConnectState(autoConnect)
|
|
42
|
-
|
|
45
|
+
if autoConnect then
|
|
46
|
+
ui:setStatus("waiting")
|
|
47
|
+
else
|
|
48
|
+
ui:setStatus("disconnected")
|
|
49
|
+
end
|
|
43
50
|
|
|
44
51
|
-- Sync state tombol toolbar dengan status widget
|
|
45
52
|
if ui.widget then
|
|
@@ -62,10 +69,29 @@ if uiModule then
|
|
|
62
69
|
if not isConnected then
|
|
63
70
|
-- CONNECT (manual) — reset manual disconnect flag agar auto-detect bisa bekerja
|
|
64
71
|
isManualDisconnect = false
|
|
65
|
-
isConnected = true
|
|
66
72
|
ui:setStatus("waiting")
|
|
67
|
-
ui:addLog("[System]
|
|
68
|
-
|
|
73
|
+
ui:addLog("[System] Menghubungkan ke Server MCP...", Color3.fromRGB(255, 220, 100))
|
|
74
|
+
|
|
75
|
+
task.spawn(function()
|
|
76
|
+
local cacheBuster = "?t=" .. tostring(os.clock())
|
|
77
|
+
local success, _ = pcall(function()
|
|
78
|
+
return HttpService:GetAsync(PING_URL .. cacheBuster)
|
|
79
|
+
end)
|
|
80
|
+
|
|
81
|
+
isConnected = true
|
|
82
|
+
if success then
|
|
83
|
+
if ui then
|
|
84
|
+
ui:setStatus("connected")
|
|
85
|
+
ui:addLog("[System] Berhasil terhubung ke Server MCP!", Color3.fromRGB(100, 255, 100))
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
if ui then
|
|
89
|
+
ui:setStatus("waiting")
|
|
90
|
+
ui:addLog("[System] Server MCP belum aktif di port 3055. Menunggu koneksi...", Color3.fromRGB(255, 200, 100))
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
startPolling()
|
|
94
|
+
end)
|
|
69
95
|
else
|
|
70
96
|
-- DISCONNECT — hentikan polling dan tandai manual disconnect
|
|
71
97
|
stopPolling()
|
|
@@ -193,8 +219,6 @@ local function processTask(taskData)
|
|
|
193
219
|
end
|
|
194
220
|
|
|
195
221
|
-- Menghentikan polling dengan bersih (dipanggil oleh tombol Disconnect)
|
|
196
|
-
local isManualDisconnect = false -- Mencegah auto-reconnect saat user sengaja disconnect
|
|
197
|
-
|
|
198
222
|
function stopPolling()
|
|
199
223
|
isConnected = false
|
|
200
224
|
isManualDisconnect = true
|
|
@@ -205,6 +229,7 @@ end
|
|
|
205
229
|
function startPolling()
|
|
206
230
|
if isPolling then return end
|
|
207
231
|
isPolling = true
|
|
232
|
+
if ui and isConnected then ui:setStatus("connected") end
|
|
208
233
|
|
|
209
234
|
task.spawn(function()
|
|
210
235
|
while isConnected do
|
|
@@ -220,7 +245,7 @@ function startPolling()
|
|
|
220
245
|
end
|
|
221
246
|
|
|
222
247
|
if success and response then
|
|
223
|
-
-- JIKA PING SUKSES, Node.js menyala (IDE / Agen Sedang Terbuka!)
|
|
248
|
+
-- JIKA PING/POLL SUKSES, Node.js menyala (IDE / Agen Sedang Terbuka!)
|
|
224
249
|
if ui then ui:setStatus("connected") end
|
|
225
250
|
|
|
226
251
|
local decodeSuccess, taskData = pcall(function()
|
|
@@ -243,7 +268,7 @@ function startPolling()
|
|
|
243
268
|
else
|
|
244
269
|
-- Fetch gagal (Node.js server mati karena IDE/Antigravity/Cursor ditutup)
|
|
245
270
|
if ui then ui:setStatus("waiting") end
|
|
246
|
-
task.wait(1)
|
|
271
|
+
task.wait(1.5)
|
|
247
272
|
end
|
|
248
273
|
|
|
249
274
|
-- Cek lagi sebelum sleep (agar disconnect lebih responsif)
|
|
@@ -257,54 +282,52 @@ end
|
|
|
257
282
|
|
|
258
283
|
-- ══════════════════════════════════════════════════════════════════════
|
|
259
284
|
-- AUTO-DETECT: Background heartbeat yang SELALU berjalan.
|
|
260
|
-
-- Mendeteksi apakah server MCP aktif setiap
|
|
261
|
-
--
|
|
262
|
-
-- Jika terdeteksi mati → otomatis update status.
|
|
285
|
+
-- Mendeteksi apakah server MCP aktif setiap 1.5 detik via PING_URL.
|
|
286
|
+
-- Menggunakan /api/ping agar instan (tidak tertahan 20 detik oleh long-poll).
|
|
263
287
|
-- ══════════════════════════════════════════════════════════════════════
|
|
264
288
|
task.spawn(function()
|
|
265
|
-
task.wait(
|
|
289
|
+
task.wait(0.5) -- Tunggu sebentar agar UI siap
|
|
266
290
|
|
|
267
291
|
if ui then
|
|
268
292
|
ui:addLog("[System] Plugin termuat. Auto-detect server MCP aktif...", Color3.fromRGB(200, 200, 200))
|
|
269
293
|
end
|
|
270
294
|
|
|
271
295
|
while true do
|
|
272
|
-
--
|
|
273
|
-
if not
|
|
274
|
-
|
|
275
|
-
local
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
isConnected
|
|
283
|
-
|
|
284
|
-
ui
|
|
285
|
-
|
|
296
|
+
-- Hanya jalankan auto-detect jika user tidak sengaja menekan Disconnect
|
|
297
|
+
if not isManualDisconnect and (autoConnect or isConnected) then
|
|
298
|
+
if not isPolling then
|
|
299
|
+
local success, _ = pcall(function()
|
|
300
|
+
local cacheBuster = "?t=" .. tostring(os.clock())
|
|
301
|
+
return HttpService:GetAsync(PING_URL .. cacheBuster)
|
|
302
|
+
end)
|
|
303
|
+
|
|
304
|
+
if success then
|
|
305
|
+
-- Server terdeteksi aktif via Ping!
|
|
306
|
+
if not isConnected then
|
|
307
|
+
isConnected = true
|
|
308
|
+
if ui then
|
|
309
|
+
ui:setStatus("connected")
|
|
310
|
+
ui:addLog("[System] Server MCP terdeteksi! Terhubung otomatis.", Color3.fromRGB(100, 255, 100))
|
|
311
|
+
end
|
|
312
|
+
startPolling()
|
|
313
|
+
else
|
|
314
|
+
startPolling()
|
|
286
315
|
end
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
316
|
+
else
|
|
317
|
+
-- Server tidak aktif / belum menyala
|
|
318
|
+
if isConnected then
|
|
319
|
+
isConnected = false
|
|
320
|
+
if ui then
|
|
321
|
+
ui:setStatus("waiting")
|
|
322
|
+
ui:addLog("[System] Server MCP terputus. Menunggu reconnect...", Color3.fromRGB(255, 200, 100))
|
|
323
|
+
end
|
|
324
|
+
elseif ui and ui.status ~= "waiting" and not isManualDisconnect then
|
|
295
325
|
ui:setStatus("waiting")
|
|
296
|
-
ui:addLog("[System] Server MCP terputus. Menunggu reconnect...", Color3.fromRGB(255, 200, 100))
|
|
297
|
-
end
|
|
298
|
-
elseif not isManualDisconnect then
|
|
299
|
-
if ui then
|
|
300
|
-
ui:setStatus("waiting")
|
|
301
|
-
-- HANYA print error jika belum terkoneksi, untuk debugging
|
|
302
|
-
ui:addLog("[Debug] Deteksi gagal: " .. tostring(errorMsg), Color3.fromRGB(150, 150, 150))
|
|
303
326
|
end
|
|
304
327
|
end
|
|
305
328
|
end
|
|
306
329
|
end
|
|
307
330
|
|
|
308
|
-
task.wait(
|
|
331
|
+
task.wait(1.5)
|
|
309
332
|
end
|
|
310
333
|
end)
|
|
@@ -381,6 +381,7 @@ end
|
|
|
381
381
|
<ProtectedString name="Source"><![CDATA[local HttpService = game:GetService("HttpService")
|
|
382
382
|
local LogService = game:GetService("LogService")
|
|
383
383
|
|
|
384
|
+
local PING_URL = "http://localhost:3055/api/ping"
|
|
384
385
|
local TASK_URL = "http://localhost:3055/api/tasks"
|
|
385
386
|
local RESPONSE_URL = "http://localhost:3055/api/response"
|
|
386
387
|
|
|
@@ -404,8 +405,10 @@ local ctx = {
|
|
|
404
405
|
}
|
|
405
406
|
local MAX_LOGS = 50
|
|
406
407
|
local isConnected = false
|
|
407
|
-
local
|
|
408
|
+
local savedAutoConnect = plugin:GetSetting("AutoConnect")
|
|
409
|
+
local autoConnect = (savedAutoConnect == nil) and true or savedAutoConnect
|
|
408
410
|
local isPolling = false
|
|
411
|
+
local isManualDisconnect = false -- Mencegah auto-reconnect saat user sengaja disconnect
|
|
409
412
|
local isClickDebounce = false -- Mencegah rapid double-click
|
|
410
413
|
|
|
411
414
|
-- Deklarasi fungsi polling di awal agar bisa dipanggil oleh UI
|
|
@@ -419,7 +422,11 @@ if uiModule then
|
|
|
419
422
|
|
|
420
423
|
-- Sync Initial State ke UI
|
|
421
424
|
ui:setAutoConnectState(autoConnect)
|
|
422
|
-
|
|
425
|
+
if autoConnect then
|
|
426
|
+
ui:setStatus("waiting")
|
|
427
|
+
else
|
|
428
|
+
ui:setStatus("disconnected")
|
|
429
|
+
end
|
|
423
430
|
|
|
424
431
|
-- Sync state tombol toolbar dengan status widget
|
|
425
432
|
if ui.widget then
|
|
@@ -442,10 +449,29 @@ if uiModule then
|
|
|
442
449
|
if not isConnected then
|
|
443
450
|
-- CONNECT (manual) — reset manual disconnect flag agar auto-detect bisa bekerja
|
|
444
451
|
isManualDisconnect = false
|
|
445
|
-
isConnected = true
|
|
446
452
|
ui:setStatus("waiting")
|
|
447
|
-
ui:addLog("[System]
|
|
448
|
-
|
|
453
|
+
ui:addLog("[System] Menghubungkan ke Server MCP...", Color3.fromRGB(255, 220, 100))
|
|
454
|
+
|
|
455
|
+
task.spawn(function()
|
|
456
|
+
local cacheBuster = "?t=" .. tostring(os.clock())
|
|
457
|
+
local success, _ = pcall(function()
|
|
458
|
+
return HttpService:GetAsync(PING_URL .. cacheBuster)
|
|
459
|
+
end)
|
|
460
|
+
|
|
461
|
+
isConnected = true
|
|
462
|
+
if success then
|
|
463
|
+
if ui then
|
|
464
|
+
ui:setStatus("connected")
|
|
465
|
+
ui:addLog("[System] Berhasil terhubung ke Server MCP!", Color3.fromRGB(100, 255, 100))
|
|
466
|
+
end
|
|
467
|
+
else
|
|
468
|
+
if ui then
|
|
469
|
+
ui:setStatus("waiting")
|
|
470
|
+
ui:addLog("[System] Server MCP belum aktif di port 3055. Menunggu koneksi...", Color3.fromRGB(255, 200, 100))
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
startPolling()
|
|
474
|
+
end)
|
|
449
475
|
else
|
|
450
476
|
-- DISCONNECT — hentikan polling dan tandai manual disconnect
|
|
451
477
|
stopPolling()
|
|
@@ -573,8 +599,6 @@ local function processTask(taskData)
|
|
|
573
599
|
end
|
|
574
600
|
|
|
575
601
|
-- Menghentikan polling dengan bersih (dipanggil oleh tombol Disconnect)
|
|
576
|
-
local isManualDisconnect = false -- Mencegah auto-reconnect saat user sengaja disconnect
|
|
577
|
-
|
|
578
602
|
function stopPolling()
|
|
579
603
|
isConnected = false
|
|
580
604
|
isManualDisconnect = true
|
|
@@ -585,6 +609,7 @@ end
|
|
|
585
609
|
function startPolling()
|
|
586
610
|
if isPolling then return end
|
|
587
611
|
isPolling = true
|
|
612
|
+
if ui and isConnected then ui:setStatus("connected") end
|
|
588
613
|
|
|
589
614
|
task.spawn(function()
|
|
590
615
|
while isConnected do
|
|
@@ -600,7 +625,7 @@ function startPolling()
|
|
|
600
625
|
end
|
|
601
626
|
|
|
602
627
|
if success and response then
|
|
603
|
-
-- JIKA PING SUKSES, Node.js menyala (IDE / Agen Sedang Terbuka!)
|
|
628
|
+
-- JIKA PING/POLL SUKSES, Node.js menyala (IDE / Agen Sedang Terbuka!)
|
|
604
629
|
if ui then ui:setStatus("connected") end
|
|
605
630
|
|
|
606
631
|
local decodeSuccess, taskData = pcall(function()
|
|
@@ -623,7 +648,7 @@ function startPolling()
|
|
|
623
648
|
else
|
|
624
649
|
-- Fetch gagal (Node.js server mati karena IDE/Antigravity/Cursor ditutup)
|
|
625
650
|
if ui then ui:setStatus("waiting") end
|
|
626
|
-
task.wait(1)
|
|
651
|
+
task.wait(1.5)
|
|
627
652
|
end
|
|
628
653
|
|
|
629
654
|
-- Cek lagi sebelum sleep (agar disconnect lebih responsif)
|
|
@@ -637,55 +662,53 @@ end
|
|
|
637
662
|
|
|
638
663
|
-- ══════════════════════════════════════════════════════════════════════
|
|
639
664
|
-- AUTO-DETECT: Background heartbeat yang SELALU berjalan.
|
|
640
|
-
-- Mendeteksi apakah server MCP aktif setiap
|
|
641
|
-
--
|
|
642
|
-
-- Jika terdeteksi mati → otomatis update status.
|
|
665
|
+
-- Mendeteksi apakah server MCP aktif setiap 1.5 detik via PING_URL.
|
|
666
|
+
-- Menggunakan /api/ping agar instan (tidak tertahan 20 detik oleh long-poll).
|
|
643
667
|
-- ══════════════════════════════════════════════════════════════════════
|
|
644
668
|
task.spawn(function()
|
|
645
|
-
task.wait(
|
|
669
|
+
task.wait(0.5) -- Tunggu sebentar agar UI siap
|
|
646
670
|
|
|
647
671
|
if ui then
|
|
648
672
|
ui:addLog("[System] Plugin termuat. Auto-detect server MCP aktif...", Color3.fromRGB(200, 200, 200))
|
|
649
673
|
end
|
|
650
674
|
|
|
651
675
|
while true do
|
|
652
|
-
--
|
|
653
|
-
if not
|
|
654
|
-
|
|
655
|
-
local
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
isConnected
|
|
663
|
-
|
|
664
|
-
ui
|
|
665
|
-
|
|
676
|
+
-- Hanya jalankan auto-detect jika user tidak sengaja menekan Disconnect
|
|
677
|
+
if not isManualDisconnect and (autoConnect or isConnected) then
|
|
678
|
+
if not isPolling then
|
|
679
|
+
local success, _ = pcall(function()
|
|
680
|
+
local cacheBuster = "?t=" .. tostring(os.clock())
|
|
681
|
+
return HttpService:GetAsync(PING_URL .. cacheBuster)
|
|
682
|
+
end)
|
|
683
|
+
|
|
684
|
+
if success then
|
|
685
|
+
-- Server terdeteksi aktif via Ping!
|
|
686
|
+
if not isConnected then
|
|
687
|
+
isConnected = true
|
|
688
|
+
if ui then
|
|
689
|
+
ui:setStatus("connected")
|
|
690
|
+
ui:addLog("[System] Server MCP terdeteksi! Terhubung otomatis.", Color3.fromRGB(100, 255, 100))
|
|
691
|
+
end
|
|
692
|
+
startPolling()
|
|
693
|
+
else
|
|
694
|
+
startPolling()
|
|
666
695
|
end
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
696
|
+
else
|
|
697
|
+
-- Server tidak aktif / belum menyala
|
|
698
|
+
if isConnected then
|
|
699
|
+
isConnected = false
|
|
700
|
+
if ui then
|
|
701
|
+
ui:setStatus("waiting")
|
|
702
|
+
ui:addLog("[System] Server MCP terputus. Menunggu reconnect...", Color3.fromRGB(255, 200, 100))
|
|
703
|
+
end
|
|
704
|
+
elseif ui and ui.status ~= "waiting" and not isManualDisconnect then
|
|
675
705
|
ui:setStatus("waiting")
|
|
676
|
-
ui:addLog("[System] Server MCP terputus. Menunggu reconnect...", Color3.fromRGB(255, 200, 100))
|
|
677
|
-
end
|
|
678
|
-
elseif not isManualDisconnect then
|
|
679
|
-
if ui then
|
|
680
|
-
ui:setStatus("waiting")
|
|
681
|
-
-- HANYA print error jika belum terkoneksi, untuk debugging
|
|
682
|
-
ui:addLog("[Debug] Deteksi gagal: " .. tostring(errorMsg), Color3.fromRGB(150, 150, 150))
|
|
683
706
|
end
|
|
684
707
|
end
|
|
685
708
|
end
|
|
686
709
|
end
|
|
687
710
|
|
|
688
|
-
task.wait(
|
|
711
|
+
task.wait(1.5)
|
|
689
712
|
end
|
|
690
713
|
end)
|
|
691
714
|
]]></ProtectedString>
|