@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 CHANGED
@@ -27,7 +27,7 @@ function backupScript(target, code) {
27
27
  // 2. Inisialisasi server MCP
28
28
  const server = new index_js_1.Server({
29
29
  name: "nvstudio-mcp",
30
- version: "2.0.1",
30
+ version: "2.0.4",
31
31
  }, {
32
32
  capabilities: {
33
33
  tools: {},
@@ -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,6 @@
1
1
  {
2
2
  "name": "@adiholick/nvstudio-mcp",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Local Bridge Server MCP for Roblox Studio",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -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 autoConnect = plugin:GetSetting("AutoConnect") or false
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
- ui:setStatus("disconnected")
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] Koneksi manual dimulai. Mencari AI Server (Waiting)...", Color3.fromRGB(255, 220, 100))
68
- startPolling()
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 2 detik.
261
- -- Jika terdeteksi aktif → otomatis connect (tanpa perlu tekan tombol).
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(1) -- Tunggu sebentar agar UI siap
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
- -- Skip heartbeat jika polling loop sudah aktif (ia sudah menangani semuanya)
273
- if not isPolling then
274
- local success, errorMsg = pcall(function()
275
- local cacheBuster = "?t=" .. tostring(os.clock())
276
- return HttpService:GetAsync(TASK_URL .. cacheBuster)
277
- end)
278
-
279
- if success then
280
- -- Server terdeteksi aktif!
281
- if not isConnected and not isManualDisconnect then
282
- isConnected = true
283
- if ui then
284
- ui:setStatus("connected")
285
- ui:addLog("[System] Server MCP terdeteksi! Auto-connected.", Color3.fromRGB(100, 255, 100))
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
- startPolling()
288
- end
289
- else
290
- -- Server tidak aktif
291
- if isConnected then
292
- -- Server baru saja mati (sebelumnya connected) — polling loop juga akan exit
293
- isConnected = false
294
- if ui then
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(2)
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 autoConnect = plugin:GetSetting("AutoConnect") or false
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
- ui:setStatus("disconnected")
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] Koneksi manual dimulai. Mencari AI Server (Waiting)...", Color3.fromRGB(255, 220, 100))
448
- startPolling()
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 2 detik.
641
- -- Jika terdeteksi aktif → otomatis connect (tanpa perlu tekan tombol).
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(1) -- Tunggu sebentar agar UI siap
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
- -- Skip heartbeat jika polling loop sudah aktif (ia sudah menangani semuanya)
653
- if not isPolling then
654
- local success, errorMsg = pcall(function()
655
- local cacheBuster = "?t=" .. tostring(os.clock())
656
- return HttpService:GetAsync(TASK_URL .. cacheBuster)
657
- end)
658
-
659
- if success then
660
- -- Server terdeteksi aktif!
661
- if not isConnected and not isManualDisconnect then
662
- isConnected = true
663
- if ui then
664
- ui:setStatus("connected")
665
- ui:addLog("[System] Server MCP terdeteksi! Auto-connected.", Color3.fromRGB(100, 255, 100))
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
- startPolling()
668
- end
669
- else
670
- -- Server tidak aktif
671
- if isConnected then
672
- -- Server baru saja mati (sebelumnya connected) — polling loop juga akan exit
673
- isConnected = false
674
- if ui then
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(2)
711
+ task.wait(1.5)
689
712
  end
690
713
  end)
691
714
  ]]></ProtectedString>