auvrynt 1.0.2 → 1.0.5
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/LICENSE +21 -21
- package/README.md +366 -233
- package/addons/auvrynt_bridge/auvrynt_bridge.gd +500 -0
- package/addons/auvrynt_bridge/plugin.cfg +7 -0
- package/bin/auvrynt-launcher.cjs +77 -0
- package/dist/artifact-registry.js +1 -0
- package/dist/aseprite-tools.js +1 -0
- package/dist/auth/connection-registry.js +145 -0
- package/dist/auth/connection-status.js +176 -0
- package/dist/auth/oauth-provider.js +617 -0
- package/dist/auth/room-registry.js +131 -0
- package/dist/auth/session-registry.js +531 -0
- package/dist/background-lifecycle.js +1 -0
- package/dist/blender-client.js +1 -109
- package/dist/blender-tools.js +1 -930
- package/dist/browser-stability.js +1 -0
- package/dist/cli/commands/help-command.js +10 -0
- package/dist/cli/commands/init-command.js +190 -0
- package/dist/cli/commands/integration-commands.js +88 -0
- package/dist/cli/commands/serve-command.js +83 -0
- package/dist/cli/commands/setup-command.js +81 -0
- package/dist/cli/commands/status-commands.js +166 -0
- package/dist/cli/commands/tunnel-command.js +127 -0
- package/dist/cli/commands/user-config-commands.js +105 -0
- package/dist/cli/dashboard-actions.js +516 -0
- package/dist/cli/dashboard-native-actions.js +282 -0
- package/dist/cli/foreground-server.js +270 -0
- package/dist/cli/instance-control.js +21 -0
- package/dist/cli/instance-lock.js +123 -0
- package/dist/cli/integration-bootstrap.js +206 -0
- package/dist/cli/lifecycle-manager.js +493 -0
- package/dist/cli/runtime-support.js +74 -0
- package/dist/cli/serena-detected-default.js +27 -0
- package/dist/cli/tunnel-health-monitor.js +182 -0
- package/dist/cli.js +82 -739
- package/dist/config.js +1 -243
- package/dist/connection-registry.js +1 -73
- package/dist/connection-status.js +1 -139
- package/dist/control-retry.js +1 -0
- package/dist/crash-recovery.js +1 -0
- package/dist/db/client.js +16 -3
- package/dist/db/schema.js +30 -0
- package/dist/dotnet-tools.js +1 -223
- package/dist/git-worktrees.js +1 -134
- package/dist/git.js +1 -41
- package/dist/godot-animation-tilemap.js +1 -82
- package/dist/godot-csharp-build.js +1 -93
- package/dist/godot-csharp-project.js +1 -157
- package/dist/godot-csharp-runner.js +1 -146
- package/dist/godot-csharp-semantic.js +1 -161
- package/dist/godot-csharp-validate.js +1 -71
- package/dist/godot-dotnet-env.js +1 -169
- package/dist/godot-editor-bridge.js +1 -140
- package/dist/godot-gdscript.js +1 -824
- package/dist/godot-project-settings.js +1 -223
- package/dist/godot-runtime-testing.js +1 -249
- package/dist/godot-tools.js +1 -183
- package/dist/http-rate-limit.js +1 -0
- package/dist/http-server-hardening.js +1 -0
- package/dist/image-tools.js +1 -157
- package/dist/infrastructure/command-reference.js +32 -0
- package/dist/infrastructure/config.js +424 -0
- package/dist/infrastructure/context7-auth.js +55 -0
- package/dist/infrastructure/http-rate-limit.js +42 -0
- package/dist/infrastructure/http-server-hardening.js +23 -0
- package/dist/infrastructure/integration-queue.js +46 -0
- package/dist/infrastructure/logger.js +129 -0
- package/dist/infrastructure/mcp-event-store.js +103 -0
- package/dist/infrastructure/ngrok-auth-pool.js +208 -0
- package/dist/infrastructure/request-context.js +14 -0
- package/dist/infrastructure/shutdown.js +37 -0
- package/dist/infrastructure/sse-heartbeat.js +47 -0
- package/dist/infrastructure/tool-capabilities.js +67 -0
- package/dist/infrastructure/tool-result-budget.js +175 -0
- package/dist/infrastructure/user-config.js +167 -0
- package/dist/integration-discovery.js +1 -98
- package/dist/integration-queue.js +1 -0
- package/dist/integrations/agent-reach/agent-reach-tools.js +196 -0
- package/dist/integrations/agent-reach/native-channels.js +468 -0
- package/dist/integrations/aseprite/aseprite-analysis-tools.js +368 -0
- package/dist/integrations/aseprite/aseprite-editing-tools.js +647 -0
- package/dist/integrations/aseprite/aseprite-live-tools.js +297 -0
- package/dist/integrations/aseprite/aseprite-safety-tools.js +594 -0
- package/dist/integrations/aseprite/aseprite-specialized-tools.js +289 -0
- package/dist/integrations/aseprite/aseprite-tools.js +837 -0
- package/dist/integrations/blender/blender-catalog-mesh.js +593 -0
- package/dist/integrations/blender/blender-catalog-object.js +906 -0
- package/dist/integrations/blender/blender-catalog-scene.js +266 -0
- package/dist/integrations/blender/blender-catalog-select.js +581 -0
- package/dist/integrations/blender/blender-catalog-stubs.js +7512 -0
- package/dist/integrations/blender/blender-catalog.js +165 -0
- package/dist/integrations/blender/blender-client.js +105 -0
- package/dist/integrations/blender/blender-tools.js +2243 -0
- package/dist/integrations/context7/context7-tools.js +106 -0
- package/dist/integrations/dotnet/dotnet-tools.js +195 -0
- package/dist/integrations/godot/godot-animation-tilemap.js +74 -0
- package/dist/integrations/godot/godot-csharp-build.js +91 -0
- package/dist/integrations/godot/godot-csharp-project.js +155 -0
- package/dist/integrations/godot/godot-csharp-runner.js +118 -0
- package/dist/integrations/godot/godot-csharp-semantic.js +146 -0
- package/dist/integrations/godot/godot-csharp-validate.js +71 -0
- package/dist/integrations/godot/godot-dotnet-env.js +134 -0
- package/dist/integrations/godot/godot-editor-bridge.js +328 -0
- package/dist/integrations/godot/godot-gdscript.js +536 -0
- package/dist/integrations/godot/godot-project-settings.js +178 -0
- package/dist/integrations/godot/godot-runtime-testing.js +263 -0
- package/dist/integrations/godot/godot-tools.js +193 -0
- package/dist/integrations/images/image-tools.js +414 -0
- package/dist/integrations/images/png-codec.js +251 -0
- package/dist/integrations/images/view-image.js +99 -0
- package/dist/integrations/images/window-capture.js +148 -0
- package/dist/integrations/integration-discovery.js +185 -0
- package/dist/integrations/obsidian/obsidian-tools.js +205 -0
- package/dist/integrations/playwright/browser-session.js +516 -0
- package/dist/integrations/playwright/browser-stability.js +36 -0
- package/dist/integrations/playwright/playwright-runtime.js +105 -0
- package/dist/integrations/playwright/web-tools.js +504 -0
- package/dist/integrations/roblox/roblox-helpers.js +381 -0
- package/dist/integrations/roblox/roblox-studio-client.js +112 -0
- package/dist/integrations/roblox/roblox-tools.js +106 -0
- package/dist/integrations/serena/serena-manager.js +464 -0
- package/dist/integrations/serena/serena-tools.js +411 -0
- package/dist/logger.js +1 -76
- package/dist/mcp-event-store.js +1 -0
- package/dist/oauth-provider.js +1 -377
- package/dist/pi-tools.js +1 -81
- package/dist/playwright-runtime.js +1 -0
- package/dist/process/background-lifecycle.js +189 -0
- package/dist/process/control-retry.js +42 -0
- package/dist/process/crash-recovery.js +23 -0
- package/dist/process/processes.js +456 -0
- package/dist/processes.js +1 -218
- package/dist/request-context.js +1 -0
- package/dist/review-checkpoints.js +1 -142
- package/dist/room-registry.js +1 -0
- package/dist/roots.js +1 -53
- package/dist/search-discovery.js +1 -271
- package/dist/serena-manager.js +1 -443
- package/dist/serena-tools.js +1 -411
- package/dist/server/dashboard-page-styles.js +331 -0
- package/dist/server/dashboard-page.js +1509 -0
- package/dist/server/dashboard-routes.js +46 -0
- package/dist/server/dashboard.js +240 -0
- package/dist/server/http-helpers.js +30 -0
- package/dist/server/integration-profile-update.js +37 -0
- package/dist/server/mcp-policy.js +169 -0
- package/dist/server/mcp-server-factory.js +127 -0
- package/dist/server/mcp-tool-registrar.js +182 -0
- package/dist/server/openai-session-hint.js +14 -0
- package/dist/server/tool-groups.js +29 -0
- package/dist/server/tool-logging.js +33 -0
- package/dist/server/tool-registration-shared.js +59 -0
- package/dist/server/tools/aseprite-advanced-tools-registration.js +547 -0
- package/dist/server/tools/aseprite-tools-registration.js +213 -0
- package/dist/server/tools/blender-tools-registration.js +690 -0
- package/dist/server/tools/core-file-tools-registration.js +459 -0
- package/dist/server/tools/dotnet-godot-window-tools-registration.js +120 -0
- package/dist/server/tools/godot-csharp-tools-registration.js +252 -0
- package/dist/server/tools/godot-gdscript-tools-registration.js +189 -0
- package/dist/server/tools/process-project-tools-registration.js +155 -0
- package/dist/server/tools/roblox-tools-registration.js +514 -0
- package/dist/server/tools/web-image-tools-registration.js +326 -0
- package/dist/server/tools/workspace-lifecycle-tools-registration.js +130 -0
- package/dist/server/ui-assets.js +97 -0
- package/dist/server/workspace-analytics.js +92 -0
- package/dist/server.js +453 -2713
- package/dist/session-registry.js +1 -0
- package/dist/shutdown.js +1 -0
- package/dist/skills.js +1 -45
- package/dist/sse-heartbeat.js +1 -0
- package/dist/tool-capabilities.js +1 -0
- package/dist/tool-result-budget.js +1 -0
- package/dist/tools/pi-tools.js +81 -0
- package/dist/tunnels/cloudflare-tunnel.js +217 -0
- package/dist/tunnels/cloudflared-service.js +174 -0
- package/dist/tunnels/ngrok-tunnel.js +220 -0
- package/dist/tunnels/tunnel-manager.js +186 -0
- package/dist/tunnels/tunnel-types.js +1 -0
- package/dist/tunnels/tunnel-utils.js +62 -0
- package/dist/ui/.vite/manifest.json +9 -2487
- package/dist/ui/assets/heavy-payload-D42A9ceL.js +1 -0
- package/dist/ui/assets/lightweight-diff-Cv4g9Hdr.js +13 -0
- package/dist/ui/assets/review-payload-DKIg1cKF.js +1 -0
- package/dist/ui/assets/workspace-app-CeK3NhmB.css +1 -0
- package/dist/ui/assets/{workspace-app-C5QsE314.js → workspace-app-CkUXnpl4.js} +6 -6
- package/dist/ui/brand-theme.js +23 -0
- package/dist/ui/workspace-app.html +15 -15
- package/dist/user-config.js +1 -57
- package/dist/view-image.js +1 -104
- package/dist/web-tools.js +1 -200
- package/dist/window-capture.js +1 -74
- package/dist/workspace/artifact-registry.js +69 -0
- package/dist/workspace/git-worktrees.js +141 -0
- package/dist/workspace/git.js +42 -0
- package/dist/workspace/review-checkpoints.js +142 -0
- package/dist/workspace/roots.js +56 -0
- package/dist/workspace/search-discovery.js +264 -0
- package/dist/workspace/skills.js +45 -0
- package/dist/workspace/workspace-store.js +219 -0
- package/dist/workspace/workspaces.js +314 -0
- package/dist/workspace-store.js +1 -125
- package/dist/workspaces.js +1 -226
- package/docs/CREATOR_STORE_FREE_ASSETS.md +162 -0
- package/docs/DEVELOPMENT_TOOLS.md +136 -93
- package/docs/assets/auvrynt-icon.png +0 -0
- package/docs/assets/auvrynt-icon.svg +20 -0
- package/docs/chatgpt-coding-workflow.md +143 -143
- package/docs/configuration.md +156 -124
- package/docs/diagnostics.md +79 -0
- package/docs/gotchas.md +244 -202
- package/docs/multi-agent-and-long-projects.md +91 -0
- package/docs/security.md +138 -100
- package/docs/setup.md +171 -131
- package/docs/source-layout.md +60 -0
- package/extensions/auvrynt_bridge/auvrynt_bridge.lua +594 -0
- package/extensions/auvrynt_bridge/live_analysis.lua +181 -0
- package/extensions/auvrynt_bridge/live_animation.lua +134 -0
- package/extensions/auvrynt_bridge/live_commands.lua +24 -0
- package/extensions/auvrynt_bridge/live_common.lua +130 -0
- package/extensions/auvrynt_bridge/live_document.lua +78 -0
- package/extensions/auvrynt_bridge/live_drawing.lua +125 -0
- package/extensions/auvrynt_bridge/live_effects.lua +91 -0
- package/extensions/auvrynt_bridge/live_palette_template.lua +167 -0
- package/extensions/auvrynt_bridge/live_selection.lua +110 -0
- package/extensions/auvrynt_bridge/live_structure.lua +129 -0
- package/extensions/auvrynt_bridge/package.json +17 -0
- package/guides/ASEPRITE_AGENT_GUIDE.md +133 -0
- package/guides/BLENDER_AGENT_GUIDE.md +472 -0
- package/guides/GODOT_AGENT_GUIDE.md +298 -0
- package/guides/PROJECT_ROUTER.md +165 -0
- package/guides/ROBLOX_AGENT_GUIDE.md +75 -0
- package/guides/SOFTWARE_AGENT_GUIDE.md +104 -0
- package/guides/WEB_AGENT_GUIDE.md +287 -0
- package/package.json +78 -70
- package/scripts/dev-server.mjs +121 -120
- package/scripts/live-tunnel-smoke.mjs +538 -0
- package/scripts/repo-hygiene.mjs +74 -0
- package/scripts/restart-after-crash.mjs +49 -0
- package/scripts/run-tests.mjs +166 -0
- package/dist/ui/assets/abap-CLvhMVsD.js +0 -1
- package/dist/ui/assets/actionscript-3--17pq3dv.js +0 -1
- package/dist/ui/assets/ada-C5qYipkI.js +0 -1
- package/dist/ui/assets/andromeeda-vGVdxbeo.js +0 -1
- package/dist/ui/assets/angular-html-BJqQFYI6.js +0 -1
- package/dist/ui/assets/angular-ts-CMJECO0Y.js +0 -1
- package/dist/ui/assets/apache-U0d_L8uA.js +0 -1
- package/dist/ui/assets/apex-CGTLDQj6.js +0 -1
- package/dist/ui/assets/apl-DWeXpBBr.js +0 -1
- package/dist/ui/assets/applescript-CCn79oCD.js +0 -1
- package/dist/ui/assets/ara-4CJ0cIlV.js +0 -1
- package/dist/ui/assets/asciidoc-DE70LPWp.js +0 -1
- package/dist/ui/assets/asm-Cmm7eHzH.js +0 -1
- package/dist/ui/assets/astro-CLbttLOq.js +0 -1
- package/dist/ui/assets/aurora-x-CDeNXAV0.js +0 -1
- package/dist/ui/assets/awk-BWXHIvNe.js +0 -1
- package/dist/ui/assets/ayu-dark-DluEY0Gj.js +0 -1
- package/dist/ui/assets/ayu-light-C3h-C4tm.js +0 -1
- package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +0 -1
- package/dist/ui/assets/ballerina-B7ZEbQpA.js +0 -1
- package/dist/ui/assets/bat-Bo4NYOV-.js +0 -1
- package/dist/ui/assets/beancount-D-usSTwE.js +0 -1
- package/dist/ui/assets/berry-DKpUyyne.js +0 -1
- package/dist/ui/assets/bibtex-Ci_nEsc7.js +0 -1
- package/dist/ui/assets/bicep-CUHmPFLl.js +0 -1
- package/dist/ui/assets/bird2-C2hNVINV.js +0 -1
- package/dist/ui/assets/blade-BOzY0P2V.js +0 -1
- package/dist/ui/assets/bsl-BkkzgIyY.js +0 -1
- package/dist/ui/assets/c-BG-8uJvh.js +0 -1
- package/dist/ui/assets/c3-CnJL0r0V.js +0 -1
- package/dist/ui/assets/cadence-CQ2zXKGN.js +0 -1
- package/dist/ui/assets/cairo-DLTphjLi.js +0 -1
- package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +0 -1
- package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +0 -1
- package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +0 -1
- package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +0 -1
- package/dist/ui/assets/clarity-SemFz856.js +0 -1
- package/dist/ui/assets/clojure-DqKBuwfJ.js +0 -1
- package/dist/ui/assets/cmake-Bj61d0ZC.js +0 -1
- package/dist/ui/assets/cobol-I_17IuGe.js +0 -1
- package/dist/ui/assets/codeowners-C8r90Shi.js +0 -1
- package/dist/ui/assets/codeql-oeQT6MSM.js +0 -1
- package/dist/ui/assets/coffee-C0B8hmok.js +0 -1
- package/dist/ui/assets/common-lisp-Cv5bFMCO.js +0 -1
- package/dist/ui/assets/coq-BrsZFFmf.js +0 -1
- package/dist/ui/assets/cpp-DDJXt6Vs.js +0 -1
- package/dist/ui/assets/crystal-Bexqace6.js +0 -1
- package/dist/ui/assets/csharp-Ct8U2NOr.js +0 -1
- package/dist/ui/assets/css-DSQhw_9R.js +0 -1
- package/dist/ui/assets/csv-Dx-8-gkx.js +0 -1
- package/dist/ui/assets/cue-CE9AQfxI.js +0 -1
- package/dist/ui/assets/cypher-ClKdZ_lG.js +0 -1
- package/dist/ui/assets/d-qD-0Kul2.js +0 -1
- package/dist/ui/assets/dark-plus-Cs2F2srj.js +0 -1
- package/dist/ui/assets/dart-DkHntEIa.js +0 -1
- package/dist/ui/assets/dax-BkyTk9wS.js +0 -1
- package/dist/ui/assets/desktop-Dlh5hvp9.js +0 -1
- package/dist/ui/assets/diff-woXpYk--.js +0 -1
- package/dist/ui/assets/docker-IyjqRm3v.js +0 -1
- package/dist/ui/assets/dotenv-_5a1GRtc.js +0 -1
- package/dist/ui/assets/dracula-BHWKrbxM.js +0 -1
- package/dist/ui/assets/dracula-soft-5eyTD99u.js +0 -1
- package/dist/ui/assets/dream-maker-DW3nJb8Q.js +0 -1
- package/dist/ui/assets/edge-Z8YYkHOr.js +0 -1
- package/dist/ui/assets/elixir-CG9uKtAF.js +0 -1
- package/dist/ui/assets/elm-GfWG2IfT.js +0 -1
- package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +0 -1
- package/dist/ui/assets/erb-xe89e73J.js +0 -1
- package/dist/ui/assets/erlang-Cphh6RMH.js +0 -1
- package/dist/ui/assets/everforest-dark-sB-x3p7T.js +0 -1
- package/dist/ui/assets/everforest-light-Df2xbC6M.js +0 -1
- package/dist/ui/assets/fennel-DQxkIbk2.js +0 -1
- package/dist/ui/assets/fish-BJitypiv.js +0 -1
- package/dist/ui/assets/fluent-C03EYrpw.js +0 -1
- package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +0 -1
- package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +0 -1
- package/dist/ui/assets/fsharp-D13ZGOAj.js +0 -1
- package/dist/ui/assets/gdresource-C0sCabJj.js +0 -1
- package/dist/ui/assets/gdscript-Cp2uCuqX.js +0 -1
- package/dist/ui/assets/gdshader-CBce3t8t.js +0 -1
- package/dist/ui/assets/genie-CV2tkWYe.js +0 -1
- package/dist/ui/assets/gherkin-DExj1W_8.js +0 -1
- package/dist/ui/assets/git-commit-BSykSTBG.js +0 -1
- package/dist/ui/assets/git-rebase-CoTL_uSE.js +0 -1
- package/dist/ui/assets/github-dark-C-LZuMrd.js +0 -1
- package/dist/ui/assets/github-dark-default-DXG-b-1a.js +0 -1
- package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +0 -1
- package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +0 -1
- package/dist/ui/assets/github-light-EUqPIrTm.js +0 -1
- package/dist/ui/assets/github-light-default-BXViO-2h.js +0 -1
- package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +0 -1
- package/dist/ui/assets/gleam-CSRkHgEL.js +0 -1
- package/dist/ui/assets/glimmer-js-DAn4UCSu.js +0 -1
- package/dist/ui/assets/glimmer-ts-Tjyi6Gh5.js +0 -1
- package/dist/ui/assets/glsl-CtTDZoqR.js +0 -1
- package/dist/ui/assets/gn-ilITqXS6.js +0 -1
- package/dist/ui/assets/gnuplot-7GGW24-e.js +0 -1
- package/dist/ui/assets/go-BJwz_mda.js +0 -1
- package/dist/ui/assets/graphql-D5gcC_Jk.js +0 -1
- package/dist/ui/assets/groovy-CacY0gHj.js +0 -1
- package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +0 -1
- package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +0 -1
- package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +0 -1
- package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +0 -1
- package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +0 -1
- package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +0 -1
- package/dist/ui/assets/hack-BJXY27dt.js +0 -1
- package/dist/ui/assets/haml-BLfFoZf7.js +0 -1
- package/dist/ui/assets/handlebars-Dw6CWkvN.js +0 -1
- package/dist/ui/assets/haskell-D8IpX4py.js +0 -1
- package/dist/ui/assets/haxe-OTjmBuCE.js +0 -1
- package/dist/ui/assets/hcl-Dh228itO.js +0 -1
- package/dist/ui/assets/heavy-payload-Dcg0-F-S.js +0 -4
- package/dist/ui/assets/hjson-CxZEssPk.js +0 -1
- package/dist/ui/assets/hlsl-Cvrh5tZx.js +0 -1
- package/dist/ui/assets/horizon-CE9ld1lL.js +0 -1
- package/dist/ui/assets/horizon-bright-Br1oVSNq.js +0 -1
- package/dist/ui/assets/houston-CsvMBhTu.js +0 -1
- package/dist/ui/assets/html-CAcAWvoN.js +0 -1
- package/dist/ui/assets/html-derivative-8aSW2E_7.js +0 -1
- package/dist/ui/assets/http-B2YR_3V1.js +0 -1
- package/dist/ui/assets/hurl-BnDJ9Zhf.js +0 -1
- package/dist/ui/assets/hxml-B0Qn7Nwc.js +0 -1
- package/dist/ui/assets/hy-CZbG8q4J.js +0 -1
- package/dist/ui/assets/imba-DsUTQ-LC.js +0 -1
- package/dist/ui/assets/ini-B5eOa1yu.js +0 -1
- package/dist/ui/assets/java-Doxi4W8u.js +0 -1
- package/dist/ui/assets/javascript-BbRdEqti.js +0 -1
- package/dist/ui/assets/jinja-CS3Tpv_Q.js +0 -1
- package/dist/ui/assets/jison-BkPGYVHR.js +0 -1
- package/dist/ui/assets/json-BMt1m3m8.js +0 -1
- package/dist/ui/assets/json5-BR5RXkoi.js +0 -1
- package/dist/ui/assets/jsonc-CYpm1nAK.js +0 -1
- package/dist/ui/assets/jsonl-CmCQp5Yx.js +0 -1
- package/dist/ui/assets/jsonnet-CJTPZ8u_.js +0 -1
- package/dist/ui/assets/jssm-DXw9l8Rf.js +0 -1
- package/dist/ui/assets/jsx-CQKrJXXY.js +0 -1
- package/dist/ui/assets/julia-BuN7i2du.js +0 -1
- package/dist/ui/assets/just-BswEPFPu.js +0 -1
- package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +0 -1
- package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +0 -1
- package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +0 -1
- package/dist/ui/assets/kdl-CsD5j6eV.js +0 -1
- package/dist/ui/assets/kotlin-DhhofPvG.js +0 -1
- package/dist/ui/assets/kusto-BUv0MjJC.js +0 -1
- package/dist/ui/assets/laserwave-C_8bwKvT.js +0 -1
- package/dist/ui/assets/latex-DU2VyFfA.js +0 -1
- package/dist/ui/assets/lean-CewbzKMR.js +0 -1
- package/dist/ui/assets/less-DVTAwKKz.js +0 -1
- package/dist/ui/assets/light-plus-DVQuIRkW.js +0 -1
- package/dist/ui/assets/liquid-Cmz1L3dq.js +0 -1
- package/dist/ui/assets/llvm-Cm23YOpf.js +0 -1
- package/dist/ui/assets/log-BNLmms1o.js +0 -1
- package/dist/ui/assets/logo-Cluzi2Zq.js +0 -1
- package/dist/ui/assets/lua-CfVXhkaJ.js +0 -1
- package/dist/ui/assets/luau-CNKltnaQ.js +0 -1
- package/dist/ui/assets/make-Dixweg8N.js +0 -1
- package/dist/ui/assets/markdown-BYOwaDjH.js +0 -1
- package/dist/ui/assets/marko-C5bfqGiA.js +0 -1
- package/dist/ui/assets/material-theme-Bm3Qr25_.js +0 -1
- package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +0 -1
- package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +0 -1
- package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +0 -1
- package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +0 -1
- package/dist/ui/assets/matlab-D7qyCx1q.js +0 -1
- package/dist/ui/assets/mdc-CVBXYfyq.js +0 -1
- package/dist/ui/assets/mdx-DQZ5AkYe.js +0 -1
- package/dist/ui/assets/mermaid-Bk4SNUv9.js +0 -1
- package/dist/ui/assets/min-dark-BSWPekZh.js +0 -1
- package/dist/ui/assets/min-light-DDpmG2fV.js +0 -1
- package/dist/ui/assets/mipsasm-BMqwQI7S.js +0 -1
- package/dist/ui/assets/mojo-BgCJLMeH.js +0 -1
- package/dist/ui/assets/monokai-CdkpiU2Y.js +0 -1
- package/dist/ui/assets/moonbit-CaWjb8XO.js +0 -1
- package/dist/ui/assets/move-B1IS1UjX.js +0 -1
- package/dist/ui/assets/narrat-_X_XdTYD.js +0 -1
- package/dist/ui/assets/nextflow-Bbiyy34d.js +0 -1
- package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +0 -1
- package/dist/ui/assets/nginx-CzJafxLa.js +0 -1
- package/dist/ui/assets/night-owl-DhmEMT88.js +0 -1
- package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +0 -1
- package/dist/ui/assets/nim-DABKtHtM.js +0 -1
- package/dist/ui/assets/nix-IvuFDN5E.js +0 -1
- package/dist/ui/assets/nord-Cb4Vim4T.js +0 -1
- package/dist/ui/assets/nushell-DcLAeLz5.js +0 -1
- package/dist/ui/assets/objective-c-D1A_Heim.js +0 -1
- package/dist/ui/assets/objective-cpp-BsSzOQcm.js +0 -1
- package/dist/ui/assets/ocaml-O90oeIOV.js +0 -1
- package/dist/ui/assets/odin-B1RWQWA5.js +0 -1
- package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +0 -1
- package/dist/ui/assets/one-light-D7Lr4KcI.js +0 -1
- package/dist/ui/assets/openscad-BUDT5pXO.js +0 -1
- package/dist/ui/assets/pascal-4ZHwLPI5.js +0 -1
- package/dist/ui/assets/perl-CBh7L3lZ.js +0 -1
- package/dist/ui/assets/php-DFbI9q5p.js +0 -1
- package/dist/ui/assets/pierre-dark-sU4Zdns8.js +0 -1
- package/dist/ui/assets/pierre-dark-soft-HXgun5vs.js +0 -1
- package/dist/ui/assets/pierre-light-DWBk51d8.js +0 -1
- package/dist/ui/assets/pierre-light-soft-CodEzPxf.js +0 -1
- package/dist/ui/assets/pkl-ot-7Btpt.js +0 -1
- package/dist/ui/assets/plastic-DQwYfKfQ.js +0 -1
- package/dist/ui/assets/plsql-DGHpHOYJ.js +0 -1
- package/dist/ui/assets/po-BiJDBrnU.js +0 -1
- package/dist/ui/assets/poimandres-DRFjx7u4.js +0 -1
- package/dist/ui/assets/polar-C7UOKdEL.js +0 -1
- package/dist/ui/assets/postcss-BXeXVLqQ.js +0 -1
- package/dist/ui/assets/powerquery-DNMTfnFr.js +0 -1
- package/dist/ui/assets/powershell-DshXNtvi.js +0 -1
- package/dist/ui/assets/prisma-BsRQq5mF.js +0 -1
- package/dist/ui/assets/prolog-iXnhIJG7.js +0 -1
- package/dist/ui/assets/proto-DB4EqR-F.js +0 -1
- package/dist/ui/assets/pug-C_PxC7gO.js +0 -1
- package/dist/ui/assets/puppet-CDv2pdJW.js +0 -1
- package/dist/ui/assets/purescript-9MfHhQsQ.js +0 -1
- package/dist/ui/assets/python-gzcpVVnB.js +0 -1
- package/dist/ui/assets/qml-DtngoFFK.js +0 -1
- package/dist/ui/assets/qmldir-DCQb3MpD.js +0 -1
- package/dist/ui/assets/qss-Fe1Jh2GI.js +0 -1
- package/dist/ui/assets/r-DohkcevW.js +0 -1
- package/dist/ui/assets/racket-DcIDlBhZ.js +0 -1
- package/dist/ui/assets/raku-B3gFvitq.js +0 -1
- package/dist/ui/assets/razor-C4t8J0_E.js +0 -1
- package/dist/ui/assets/red-CJ3rzSJv.js +0 -1
- package/dist/ui/assets/reg-CRGYupPL.js +0 -1
- package/dist/ui/assets/regexp-OGbTbjgY.js +0 -1
- package/dist/ui/assets/rel-BtDbiS_P.js +0 -1
- package/dist/ui/assets/review-payload-C5Qz60Kq.js +0 -1
- package/dist/ui/assets/riscv-Ckw8ddFX.js +0 -1
- package/dist/ui/assets/ron-VUp2lXgN.js +0 -1
- package/dist/ui/assets/rose-pine-BthvhNj6.js +0 -1
- package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +0 -1
- package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +0 -1
- package/dist/ui/assets/rosmsg-CAekHB0j.js +0 -1
- package/dist/ui/assets/rst-CfBbbcvC.js +0 -1
- package/dist/ui/assets/ruby-Bw0mpxHY.js +0 -1
- package/dist/ui/assets/rust-Cfkwpbl8.js +0 -1
- package/dist/ui/assets/sas-Cs3s9JOf.js +0 -1
- package/dist/ui/assets/sass-DXrisJhu.js +0 -1
- package/dist/ui/assets/scala-DKOlJaKm.js +0 -1
- package/dist/ui/assets/scheme-DQCgrYNe.js +0 -1
- package/dist/ui/assets/scss-sRxNJS54.js +0 -1
- package/dist/ui/assets/sdbl-bTVj8UrX.js +0 -1
- package/dist/ui/assets/shaderlab-TOUzSsQk.js +0 -1
- package/dist/ui/assets/shellscript-B0X6wh0R.js +0 -1
- package/dist/ui/assets/shellsession-D2FdOioR.js +0 -1
- package/dist/ui/assets/slack-dark-DnToyrRv.js +0 -1
- package/dist/ui/assets/slack-ochin-B2OO5cIa.js +0 -1
- package/dist/ui/assets/smalltalk-B16xEiuN.js +0 -1
- package/dist/ui/assets/snazzy-light-4G7pJPwS.js +0 -1
- package/dist/ui/assets/solarized-dark-DV17i1UV.js +0 -1
- package/dist/ui/assets/solarized-light-DSh2HLQt.js +0 -1
- package/dist/ui/assets/solidity-CKzVLygQ.js +0 -1
- package/dist/ui/assets/soy-s6g9OjJ1.js +0 -1
- package/dist/ui/assets/sparql-D_iOobhT.js +0 -1
- package/dist/ui/assets/splunk-BC2Px7Mm.js +0 -1
- package/dist/ui/assets/sql-DVTnh5V-.js +0 -1
- package/dist/ui/assets/ssh-config-BgfXC-Er.js +0 -1
- package/dist/ui/assets/stata-DFIDiXF9.js +0 -1
- package/dist/ui/assets/stylus-B6D30XZt.js +0 -1
- package/dist/ui/assets/surrealql-DVfrsLyg.js +0 -1
- package/dist/ui/assets/svelte-D5GsxESq.js +0 -1
- package/dist/ui/assets/swift-DonLKvLd.js +0 -1
- package/dist/ui/assets/synthwave-84-nFMaYfgc.js +0 -1
- package/dist/ui/assets/system-verilog-DJ5XKQeo.js +0 -1
- package/dist/ui/assets/systemd-BxMlprV5.js +0 -1
- package/dist/ui/assets/talonscript-CohzipZa.js +0 -1
- package/dist/ui/assets/tasl-DMoTqEGO.js +0 -1
- package/dist/ui/assets/tcl-CZd0xW_V.js +0 -1
- package/dist/ui/assets/templ-DI-Zyqzd.js +0 -1
- package/dist/ui/assets/terraform-DswuEJGm.js +0 -1
- package/dist/ui/assets/tex-WHHmlbJI.js +0 -1
- package/dist/ui/assets/tokyo-night-oM2G3aXe.js +0 -1
- package/dist/ui/assets/toml-CcmNWLt0.js +0 -1
- package/dist/ui/assets/ts-tags-Dwpo2RFJ.js +0 -1
- package/dist/ui/assets/tsv-sltzmVWM.js +0 -1
- package/dist/ui/assets/tsx-ph_idaAW.js +0 -1
- package/dist/ui/assets/turtle-ByJddavk.js +0 -1
- package/dist/ui/assets/twig-YO17jFjR.js +0 -1
- package/dist/ui/assets/typescript-CbAxjhkl.js +0 -1
- package/dist/ui/assets/typespec-B88KGewJ.js +0 -1
- package/dist/ui/assets/typst-DI99ib-x.js +0 -1
- package/dist/ui/assets/useFileDiffInstance-B2DLX3Vg.js +0 -244
- package/dist/ui/assets/v-DETTlOr0.js +0 -1
- package/dist/ui/assets/vala-zf12oZj6.js +0 -1
- package/dist/ui/assets/vb-Djn5o6TS.js +0 -1
- package/dist/ui/assets/verilog-CiiDBU1e.js +0 -1
- package/dist/ui/assets/vesper-D5bVUKB1.js +0 -1
- package/dist/ui/assets/vhdl-BroJfC0k.js +0 -1
- package/dist/ui/assets/viml-DvXPmvsu.js +0 -1
- package/dist/ui/assets/vitesse-black-fwtXNY1n.js +0 -1
- package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +0 -1
- package/dist/ui/assets/vitesse-light-VbXTXTou.js +0 -1
- package/dist/ui/assets/vue-CraHL-Ps.js +0 -1
- package/dist/ui/assets/vue-html-e9tyZNtS.js +0 -1
- package/dist/ui/assets/vue-vine-CofmxaUb.js +0 -1
- package/dist/ui/assets/vyper-CgoNMtux.js +0 -1
- package/dist/ui/assets/wasm-BnjxR4X6.js +0 -1
- package/dist/ui/assets/wasm-ByWQv1Qj.js +0 -1
- package/dist/ui/assets/wenyan-C8pVoKbM.js +0 -1
- package/dist/ui/assets/wgsl-BsKzXJz4.js +0 -1
- package/dist/ui/assets/wikitext-ClFFjSW2.js +0 -1
- package/dist/ui/assets/wit-DdvCle-K.js +0 -1
- package/dist/ui/assets/wolfram-DLL8P-h_.js +0 -1
- package/dist/ui/assets/workspace-app-8--oTbCd.css +0 -1
- package/dist/ui/assets/xml-BWSdPZHm.js +0 -1
- package/dist/ui/assets/xsl-BtCt7Hx8.js +0 -1
- package/dist/ui/assets/yaml-BZiTw7cQ.js +0 -1
- package/dist/ui/assets/zenscript-BnlCZFoB.js +0 -1
- package/dist/ui/assets/zig-CMLA9XwU.js +0 -1
- package/docs/assets/auvrynt-logo-light.png +0 -0
- package/docs/assets/auvrynt-screenshot.png +0 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { callRobloxStudioTool } from "./roblox-studio-client.js";
|
|
2
|
+
function jsonForLuau(value) {
|
|
3
|
+
return JSON.stringify(value);
|
|
4
|
+
}
|
|
5
|
+
/** Resolve a dotted path like Workspace.Part or game.Workspace.Part in Luau. */
|
|
6
|
+
const RESOLVE_PATH_FN = `
|
|
7
|
+
local function resolvePath(path)
|
|
8
|
+
if typeof(path) ~= "string" or path == "" then
|
|
9
|
+
error("path must be a non-empty string")
|
|
10
|
+
end
|
|
11
|
+
local parts = string.split(path, ".")
|
|
12
|
+
local i = 1
|
|
13
|
+
if parts[1] == "game" then
|
|
14
|
+
i = 2
|
|
15
|
+
end
|
|
16
|
+
local cur = game
|
|
17
|
+
if parts[i] == "Place1" or parts[i] == "game" then
|
|
18
|
+
i = i + 1
|
|
19
|
+
end
|
|
20
|
+
while i <= #parts do
|
|
21
|
+
local name = parts[i]
|
|
22
|
+
if name == "" then
|
|
23
|
+
error("empty path segment in " .. path)
|
|
24
|
+
end
|
|
25
|
+
local nextInst = cur:FindFirstChild(name)
|
|
26
|
+
if not nextInst then
|
|
27
|
+
-- services are often direct children of DataModel via GetService
|
|
28
|
+
local ok, svc = pcall(function()
|
|
29
|
+
return game:GetService(name)
|
|
30
|
+
end)
|
|
31
|
+
if ok and svc and (cur == game or cur == game:GetService("Workspace") and false) then
|
|
32
|
+
nextInst = svc
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
if not nextInst and cur == game then
|
|
36
|
+
local ok, svc = pcall(function()
|
|
37
|
+
return game:GetService(name)
|
|
38
|
+
end)
|
|
39
|
+
if ok then nextInst = svc end
|
|
40
|
+
end
|
|
41
|
+
if not nextInst then
|
|
42
|
+
error("could not resolve '" .. name .. "' under " .. cur:GetFullName() .. " (from " .. path .. ")")
|
|
43
|
+
end
|
|
44
|
+
cur = nextInst
|
|
45
|
+
i = i + 1
|
|
46
|
+
end
|
|
47
|
+
return cur
|
|
48
|
+
end
|
|
49
|
+
`;
|
|
50
|
+
const APPLY_PROPS_FN = `
|
|
51
|
+
local function parseColor3(v)
|
|
52
|
+
if typeof(v) == "table" then
|
|
53
|
+
if v.R ~= nil then return Color3.new(v.R, v.G or 0, v.B or 0) end
|
|
54
|
+
if v[1] ~= nil then return Color3.fromRGB(v[1], v[2] or 0, v[3] or 0) end
|
|
55
|
+
end
|
|
56
|
+
if typeof(v) == "string" then
|
|
57
|
+
local r, g, b = string.match(v, "^(%d+)%s*,%s*(%d+)%s*,%s*(%d+)$")
|
|
58
|
+
if r then return Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b)) end
|
|
59
|
+
end
|
|
60
|
+
return nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
local function parseVector3(v)
|
|
64
|
+
if typeof(v) == "table" then
|
|
65
|
+
if v.X ~= nil then return Vector3.new(v.X, v.Y or 0, v.Z or 0) end
|
|
66
|
+
if v[1] ~= nil then return Vector3.new(v[1], v[2] or 0, v[3] or 0) end
|
|
67
|
+
end
|
|
68
|
+
return nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
local function applyProps(inst, props)
|
|
72
|
+
if typeof(props) ~= "table" then return end
|
|
73
|
+
for key, value in pairs(props) do
|
|
74
|
+
if key == "Parent" then
|
|
75
|
+
-- ignore Parent here; use set_parent helper
|
|
76
|
+
elseif typeof(value) == "table" and value.__enum ~= nil then
|
|
77
|
+
local enumName, itemName = string.match(tostring(value.__enum), "([^%.]+)%.(.+)")
|
|
78
|
+
if not enumName then
|
|
79
|
+
enumName = value.__enum
|
|
80
|
+
itemName = value.item or value.name
|
|
81
|
+
end
|
|
82
|
+
local ok, enumType = pcall(function() return Enum[enumName] end)
|
|
83
|
+
if ok and enumType and itemName then
|
|
84
|
+
inst[key] = enumType[itemName]
|
|
85
|
+
end
|
|
86
|
+
elseif key == "Color" or key == "Color3" or key == "BackgroundColor3" or key == "TextColor3" or key == "ImageColor3" or key == "Ambient" or key == "OutdoorAmbient" or key == "FogColor" then
|
|
87
|
+
local c = parseColor3(value)
|
|
88
|
+
if c then inst[key] = c end
|
|
89
|
+
elseif key == "Size" or key == "Position" or key == "Orientation" then
|
|
90
|
+
local vec = parseVector3(value)
|
|
91
|
+
if vec then
|
|
92
|
+
if key == "Position" and inst:IsA("BasePart") then
|
|
93
|
+
inst.Position = vec
|
|
94
|
+
elseif key == "Size" and inst:IsA("BasePart") then
|
|
95
|
+
inst.Size = vec
|
|
96
|
+
elseif key == "Orientation" and inst:IsA("BasePart") then
|
|
97
|
+
inst.Orientation = vec
|
|
98
|
+
else
|
|
99
|
+
pcall(function() inst[key] = vec end)
|
|
100
|
+
end
|
|
101
|
+
elseif typeof(value) ~= "table" then
|
|
102
|
+
pcall(function() inst[key] = value end)
|
|
103
|
+
end
|
|
104
|
+
elseif key == "CFrame" and typeof(value) == "table" then
|
|
105
|
+
local p = parseVector3(value.Position or value.position or value)
|
|
106
|
+
if p and (value.Position or value.position) then
|
|
107
|
+
inst.CFrame = CFrame.new(p)
|
|
108
|
+
elseif value[1] then
|
|
109
|
+
inst.CFrame = CFrame.new(value[1], value[2] or 0, value[3] or 0)
|
|
110
|
+
end
|
|
111
|
+
else
|
|
112
|
+
pcall(function() inst[key] = value end)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
`;
|
|
117
|
+
async function runLuau(code, datamodelType = "Edit") {
|
|
118
|
+
return callRobloxStudioTool("execute_luau", {
|
|
119
|
+
code,
|
|
120
|
+
datamodel_type: datamodelType,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
export async function robloxCreateInstance(opts) {
|
|
124
|
+
const name = opts.name ?? opts.className;
|
|
125
|
+
const props = opts.properties ?? {};
|
|
126
|
+
const code = `
|
|
127
|
+
${RESOLVE_PATH_FN}
|
|
128
|
+
${APPLY_PROPS_FN}
|
|
129
|
+
local parent = resolvePath(${jsonForLuau(opts.parentPath)})
|
|
130
|
+
local inst = Instance.new(${jsonForLuau(opts.className)})
|
|
131
|
+
inst.Name = ${jsonForLuau(name)}
|
|
132
|
+
applyProps(inst, game.HttpService:JSONDecode(${jsonForLuau(JSON.stringify(props))}))
|
|
133
|
+
inst.Parent = parent
|
|
134
|
+
return { ok = true, path = inst:GetFullName(), className = inst.ClassName, name = inst.Name }
|
|
135
|
+
`;
|
|
136
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
137
|
+
}
|
|
138
|
+
export async function robloxDestroyInstance(opts) {
|
|
139
|
+
const code = `
|
|
140
|
+
${RESOLVE_PATH_FN}
|
|
141
|
+
local inst = resolvePath(${jsonForLuau(opts.path)})
|
|
142
|
+
local full = inst:GetFullName()
|
|
143
|
+
inst:Destroy()
|
|
144
|
+
return { ok = true, destroyed = full }
|
|
145
|
+
`;
|
|
146
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
147
|
+
}
|
|
148
|
+
export async function robloxCloneInstance(opts) {
|
|
149
|
+
const code = `
|
|
150
|
+
${RESOLVE_PATH_FN}
|
|
151
|
+
local src = resolvePath(${jsonForLuau(opts.path)})
|
|
152
|
+
local clone = src:Clone()
|
|
153
|
+
${opts.name ? `clone.Name = ${jsonForLuau(opts.name)}` : ""}
|
|
154
|
+
local parent = ${opts.parentPath ? `resolvePath(${jsonForLuau(opts.parentPath)})` : "src.Parent"}
|
|
155
|
+
clone.Parent = parent
|
|
156
|
+
return { ok = true, path = clone:GetFullName(), name = clone.Name, className = clone.ClassName }
|
|
157
|
+
`;
|
|
158
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
159
|
+
}
|
|
160
|
+
export async function robloxSetParent(opts) {
|
|
161
|
+
const code = `
|
|
162
|
+
${RESOLVE_PATH_FN}
|
|
163
|
+
local inst = resolvePath(${jsonForLuau(opts.path)})
|
|
164
|
+
local parent = resolvePath(${jsonForLuau(opts.parentPath)})
|
|
165
|
+
inst.Parent = parent
|
|
166
|
+
return { ok = true, path = inst:GetFullName(), parent = parent:GetFullName() }
|
|
167
|
+
`;
|
|
168
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
169
|
+
}
|
|
170
|
+
export async function robloxRenameInstance(opts) {
|
|
171
|
+
const code = `
|
|
172
|
+
${RESOLVE_PATH_FN}
|
|
173
|
+
local inst = resolvePath(${jsonForLuau(opts.path)})
|
|
174
|
+
inst.Name = ${jsonForLuau(opts.name)}
|
|
175
|
+
return { ok = true, path = inst:GetFullName(), name = inst.Name }
|
|
176
|
+
`;
|
|
177
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
178
|
+
}
|
|
179
|
+
export async function robloxSetProperties(opts) {
|
|
180
|
+
const code = `
|
|
181
|
+
${RESOLVE_PATH_FN}
|
|
182
|
+
${APPLY_PROPS_FN}
|
|
183
|
+
local inst = resolvePath(${jsonForLuau(opts.path)})
|
|
184
|
+
applyProps(inst, game.HttpService:JSONDecode(${jsonForLuau(JSON.stringify(opts.properties))}))
|
|
185
|
+
return { ok = true, path = inst:GetFullName() }
|
|
186
|
+
`;
|
|
187
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
188
|
+
}
|
|
189
|
+
export async function robloxSetAttribute(opts) {
|
|
190
|
+
const code = `
|
|
191
|
+
${RESOLVE_PATH_FN}
|
|
192
|
+
local inst = resolvePath(${jsonForLuau(opts.path)})
|
|
193
|
+
local value = game.HttpService:JSONDecode(${jsonForLuau(JSON.stringify({ v: opts.value }))}).v
|
|
194
|
+
inst:SetAttribute(${jsonForLuau(opts.key)}, value)
|
|
195
|
+
return { ok = true, path = inst:GetFullName(), key = ${jsonForLuau(opts.key)} }
|
|
196
|
+
`;
|
|
197
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
198
|
+
}
|
|
199
|
+
export async function robloxSetLighting(opts) {
|
|
200
|
+
const code = `
|
|
201
|
+
${APPLY_PROPS_FN}
|
|
202
|
+
local lighting = game:GetService("Lighting")
|
|
203
|
+
applyProps(lighting, game.HttpService:JSONDecode(${jsonForLuau(JSON.stringify(opts.properties))}))
|
|
204
|
+
return { ok = true, path = lighting:GetFullName() }
|
|
205
|
+
`;
|
|
206
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
207
|
+
}
|
|
208
|
+
export async function robloxFillTerrainRegion(opts) {
|
|
209
|
+
const material = opts.material ?? "Grass";
|
|
210
|
+
const resolution = opts.resolution ?? 4;
|
|
211
|
+
const code = `
|
|
212
|
+
local terrain = workspace.Terrain
|
|
213
|
+
local mat = Enum.Material[${jsonForLuau(material)}]
|
|
214
|
+
local cf = CFrame.new(${opts.x}, ${opts.y}, ${opts.z})
|
|
215
|
+
local size = Vector3.new(${opts.sizeX}, ${opts.sizeY}, ${opts.sizeZ})
|
|
216
|
+
terrain:FillBlock(cf, size, mat)
|
|
217
|
+
return { ok = true, material = ${jsonForLuau(material)}, center = {${opts.x}, ${opts.y}, ${opts.z}}, size = {${opts.sizeX}, ${opts.sizeY}, ${opts.sizeZ}} }
|
|
218
|
+
`;
|
|
219
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
220
|
+
}
|
|
221
|
+
export async function robloxClearTerrain(opts = {}) {
|
|
222
|
+
const code = `
|
|
223
|
+
workspace.Terrain:Clear()
|
|
224
|
+
return { ok = true }
|
|
225
|
+
`;
|
|
226
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
227
|
+
}
|
|
228
|
+
export async function robloxCreateScreenGui(opts) {
|
|
229
|
+
const name = opts.name ?? "ScreenGui";
|
|
230
|
+
const parentPath = opts.parentPath ?? "StarterGui";
|
|
231
|
+
const resetOnSpawn = opts.resetOnSpawn !== false;
|
|
232
|
+
const code = `
|
|
233
|
+
${RESOLVE_PATH_FN}
|
|
234
|
+
local parent = resolvePath(${jsonForLuau(parentPath)})
|
|
235
|
+
local gui = Instance.new("ScreenGui")
|
|
236
|
+
gui.Name = ${jsonForLuau(name)}
|
|
237
|
+
gui.ResetOnSpawn = ${resetOnSpawn ? "true" : "false"}
|
|
238
|
+
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
|
|
239
|
+
gui.Parent = parent
|
|
240
|
+
return { ok = true, path = gui:GetFullName() }
|
|
241
|
+
`;
|
|
242
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
243
|
+
}
|
|
244
|
+
export async function robloxEnsurePostEffect(opts) {
|
|
245
|
+
const name = opts.name ?? opts.className;
|
|
246
|
+
const props = opts.properties ?? {};
|
|
247
|
+
const code = `
|
|
248
|
+
${APPLY_PROPS_FN}
|
|
249
|
+
local lighting = game:GetService("Lighting")
|
|
250
|
+
local effect = lighting:FindFirstChild(${jsonForLuau(name)})
|
|
251
|
+
if not effect or effect.ClassName ~= ${jsonForLuau(opts.className)} then
|
|
252
|
+
if effect then effect:Destroy() end
|
|
253
|
+
effect = Instance.new(${jsonForLuau(opts.className)})
|
|
254
|
+
effect.Name = ${jsonForLuau(name)}
|
|
255
|
+
effect.Parent = lighting
|
|
256
|
+
end
|
|
257
|
+
applyProps(effect, game.HttpService:JSONDecode(${jsonForLuau(JSON.stringify(props))}))
|
|
258
|
+
return { ok = true, path = effect:GetFullName(), className = effect.ClassName }
|
|
259
|
+
`;
|
|
260
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
261
|
+
}
|
|
262
|
+
export async function robloxCreateGuiObject(opts) {
|
|
263
|
+
return robloxCreateInstance({
|
|
264
|
+
className: opts.className,
|
|
265
|
+
parentPath: opts.parentPath,
|
|
266
|
+
name: opts.name,
|
|
267
|
+
properties: opts.properties,
|
|
268
|
+
datamodelType: opts.datamodelType,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
export async function robloxCreateSound(opts) {
|
|
272
|
+
const props = { ...(opts.properties ?? {}) };
|
|
273
|
+
if (opts.soundId)
|
|
274
|
+
props.SoundId = opts.soundId.startsWith("rbxassetid://") ? opts.soundId : `rbxassetid://${opts.soundId}`;
|
|
275
|
+
return robloxCreateInstance({
|
|
276
|
+
className: "Sound",
|
|
277
|
+
parentPath: opts.parentPath,
|
|
278
|
+
name: opts.name ?? "Sound",
|
|
279
|
+
properties: props,
|
|
280
|
+
datamodelType: opts.datamodelType,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
export async function robloxCreateConstraint(opts) {
|
|
284
|
+
const name = opts.name ?? opts.className;
|
|
285
|
+
const props = opts.properties ?? {};
|
|
286
|
+
const code = `
|
|
287
|
+
${RESOLVE_PATH_FN}
|
|
288
|
+
${APPLY_PROPS_FN}
|
|
289
|
+
local parent = resolvePath(${jsonForLuau(opts.parentPath)})
|
|
290
|
+
local c = Instance.new(${jsonForLuau(opts.className)})
|
|
291
|
+
c.Name = ${jsonForLuau(name)}
|
|
292
|
+
applyProps(c, game.HttpService:JSONDecode(${jsonForLuau(JSON.stringify(props))}))
|
|
293
|
+
${opts.attachment0Path ? `c.Attachment0 = resolvePath(${jsonForLuau(opts.attachment0Path)})` : ""}
|
|
294
|
+
${opts.attachment1Path ? `c.Attachment1 = resolvePath(${jsonForLuau(opts.attachment1Path)})` : ""}
|
|
295
|
+
${opts.part0Path ? `pcall(function() c.Part0 = resolvePath(${jsonForLuau(opts.part0Path)}) end)` : ""}
|
|
296
|
+
${opts.part1Path ? `pcall(function() c.Part1 = resolvePath(${jsonForLuau(opts.part1Path)}) end)` : ""}
|
|
297
|
+
c.Parent = parent
|
|
298
|
+
return { ok = true, path = c:GetFullName(), className = c.ClassName }
|
|
299
|
+
`;
|
|
300
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
301
|
+
}
|
|
302
|
+
export async function robloxSetCollisionGroup(opts) {
|
|
303
|
+
const code = `
|
|
304
|
+
${RESOLVE_PATH_FN}
|
|
305
|
+
local inst = resolvePath(${jsonForLuau(opts.path)})
|
|
306
|
+
local ps = game:GetService("PhysicsService")
|
|
307
|
+
local group = ${jsonForLuau(opts.groupName)}
|
|
308
|
+
local ok = pcall(function()
|
|
309
|
+
ps:RegisterCollisionGroup(group)
|
|
310
|
+
end)
|
|
311
|
+
if inst:IsA("BasePart") then
|
|
312
|
+
inst.CollisionGroup = group
|
|
313
|
+
else
|
|
314
|
+
error("path must be a BasePart")
|
|
315
|
+
end
|
|
316
|
+
return { ok = true, path = inst:GetFullName(), collisionGroup = group }
|
|
317
|
+
`;
|
|
318
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
319
|
+
}
|
|
320
|
+
export async function robloxSetCollisionGroupCollidable(opts) {
|
|
321
|
+
const code = `
|
|
322
|
+
local ps = game:GetService("PhysicsService")
|
|
323
|
+
pcall(function() ps:RegisterCollisionGroup(${jsonForLuau(opts.groupA)}) end)
|
|
324
|
+
pcall(function() ps:RegisterCollisionGroup(${jsonForLuau(opts.groupB)}) end)
|
|
325
|
+
ps:CollisionGroupSetCollidable(${jsonForLuau(opts.groupA)}, ${jsonForLuau(opts.groupB)}, ${opts.collidable ? "true" : "false"})
|
|
326
|
+
return { ok = true, groupA = ${jsonForLuau(opts.groupA)}, groupB = ${jsonForLuau(opts.groupB)}, collidable = ${opts.collidable ? "true" : "false"} }
|
|
327
|
+
`;
|
|
328
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
329
|
+
}
|
|
330
|
+
export async function robloxFillTerrainBall(opts) {
|
|
331
|
+
const material = opts.material ?? "Grass";
|
|
332
|
+
const code = `
|
|
333
|
+
local mat = Enum.Material[${jsonForLuau(material)}]
|
|
334
|
+
workspace.Terrain:FillBall(Vector3.new(${opts.x}, ${opts.y}, ${opts.z}), ${opts.radius}, mat)
|
|
335
|
+
return { ok = true, shape = "ball", material = ${jsonForLuau(material)} }
|
|
336
|
+
`;
|
|
337
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
338
|
+
}
|
|
339
|
+
export async function robloxSetWorkspaceGravity(opts) {
|
|
340
|
+
const code = `
|
|
341
|
+
workspace.Gravity = ${opts.gravity}
|
|
342
|
+
return { ok = true, gravity = workspace.Gravity }
|
|
343
|
+
`;
|
|
344
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
345
|
+
}
|
|
346
|
+
export async function robloxCreateRemoteEvent(opts) {
|
|
347
|
+
return robloxCreateInstance({
|
|
348
|
+
className: "RemoteEvent",
|
|
349
|
+
parentPath: opts.parentPath ?? "ReplicatedStorage",
|
|
350
|
+
name: opts.name,
|
|
351
|
+
datamodelType: opts.datamodelType,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
export async function robloxCreateRemoteFunction(opts) {
|
|
355
|
+
return robloxCreateInstance({
|
|
356
|
+
className: "RemoteFunction",
|
|
357
|
+
parentPath: opts.parentPath ?? "ReplicatedStorage",
|
|
358
|
+
name: opts.name,
|
|
359
|
+
datamodelType: opts.datamodelType,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
export async function robloxCreateModuleScript(opts) {
|
|
363
|
+
const source = opts.source ?? "local module = {}\n\nreturn module\n";
|
|
364
|
+
const code = `
|
|
365
|
+
${RESOLVE_PATH_FN}
|
|
366
|
+
local parent = resolvePath(${jsonForLuau(opts.parentPath)})
|
|
367
|
+
local existing = parent:FindFirstChild(${jsonForLuau(opts.name)})
|
|
368
|
+
local mod
|
|
369
|
+
if existing and existing:IsA("ModuleScript") then
|
|
370
|
+
mod = existing
|
|
371
|
+
else
|
|
372
|
+
if existing then existing:Destroy() end
|
|
373
|
+
mod = Instance.new("ModuleScript")
|
|
374
|
+
mod.Name = ${jsonForLuau(opts.name)}
|
|
375
|
+
mod.Parent = parent
|
|
376
|
+
end
|
|
377
|
+
mod.Source = ${jsonForLuau(source)}
|
|
378
|
+
return { ok = true, path = mod:GetFullName() }
|
|
379
|
+
`;
|
|
380
|
+
return runLuau(code, opts.datamodelType ?? "Edit");
|
|
381
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
6
|
+
let client;
|
|
7
|
+
let transport;
|
|
8
|
+
let connectPromise;
|
|
9
|
+
let cachedTools;
|
|
10
|
+
export function resolveRobloxStudioMcpLaunch() {
|
|
11
|
+
if (process.platform === "win32") {
|
|
12
|
+
const bat = join(process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"), "Roblox", "mcp.bat");
|
|
13
|
+
return {
|
|
14
|
+
command: "cmd.exe",
|
|
15
|
+
args: ["/c", bat],
|
|
16
|
+
pathExists: existsSync(bat),
|
|
17
|
+
platform: process.platform,
|
|
18
|
+
hint: bat,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (process.platform === "darwin") {
|
|
22
|
+
const bin = "/Applications/RobloxStudio.app/Contents/MacOS/StudioMCP";
|
|
23
|
+
return {
|
|
24
|
+
command: bin,
|
|
25
|
+
args: [],
|
|
26
|
+
pathExists: existsSync(bin),
|
|
27
|
+
platform: process.platform,
|
|
28
|
+
hint: bin,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Linux / other: Studio MCP is not officially documented; surface a clear status.
|
|
32
|
+
const bat = join(homedir(), ".local", "share", "Roblox", "mcp.bat");
|
|
33
|
+
return {
|
|
34
|
+
command: bat,
|
|
35
|
+
args: [],
|
|
36
|
+
pathExists: existsSync(bat),
|
|
37
|
+
platform: process.platform,
|
|
38
|
+
hint: "Official Roblox Studio MCP is documented for Windows and macOS only.",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export async function getRobloxStudioClient() {
|
|
42
|
+
if (client)
|
|
43
|
+
return client;
|
|
44
|
+
if (connectPromise)
|
|
45
|
+
return connectPromise;
|
|
46
|
+
connectPromise = (async () => {
|
|
47
|
+
const launch = resolveRobloxStudioMcpLaunch();
|
|
48
|
+
if (!launch.pathExists) {
|
|
49
|
+
throw new Error(`Roblox Studio MCP launcher not found at ${launch.hint}. Install the latest Roblox Studio, enable Assistant → Manage MCP Servers → Enable Studio as MCP server, then retry.`);
|
|
50
|
+
}
|
|
51
|
+
const nextTransport = new StdioClientTransport({
|
|
52
|
+
command: launch.command,
|
|
53
|
+
args: launch.args,
|
|
54
|
+
stderr: "pipe",
|
|
55
|
+
});
|
|
56
|
+
const nextClient = new Client({ name: "auvrynt-roblox-proxy", version: "1.0.0" }, { capabilities: {} });
|
|
57
|
+
nextTransport.onclose = () => {
|
|
58
|
+
client = undefined;
|
|
59
|
+
transport = undefined;
|
|
60
|
+
cachedTools = undefined;
|
|
61
|
+
connectPromise = undefined;
|
|
62
|
+
};
|
|
63
|
+
await nextClient.connect(nextTransport);
|
|
64
|
+
client = nextClient;
|
|
65
|
+
transport = nextTransport;
|
|
66
|
+
return nextClient;
|
|
67
|
+
})();
|
|
68
|
+
try {
|
|
69
|
+
return await connectPromise;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
connectPromise = undefined;
|
|
73
|
+
client = undefined;
|
|
74
|
+
transport = undefined;
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function disconnectRobloxStudioClient() {
|
|
79
|
+
const current = transport;
|
|
80
|
+
client = undefined;
|
|
81
|
+
transport = undefined;
|
|
82
|
+
cachedTools = undefined;
|
|
83
|
+
connectPromise = undefined;
|
|
84
|
+
if (current) {
|
|
85
|
+
try {
|
|
86
|
+
await current.close();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// ignore close errors
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function listRobloxStudioTools(forceRefresh = false) {
|
|
94
|
+
if (cachedTools && !forceRefresh)
|
|
95
|
+
return cachedTools;
|
|
96
|
+
const c = await getRobloxStudioClient();
|
|
97
|
+
const result = await c.listTools();
|
|
98
|
+
cachedTools = (result.tools ?? []).map((tool) => ({
|
|
99
|
+
name: tool.name,
|
|
100
|
+
description: tool.description,
|
|
101
|
+
inputSchema: tool.inputSchema,
|
|
102
|
+
}));
|
|
103
|
+
return cachedTools;
|
|
104
|
+
}
|
|
105
|
+
export async function callRobloxStudioTool(name, args = {}) {
|
|
106
|
+
const c = await getRobloxStudioClient();
|
|
107
|
+
const result = await c.callTool({ name, arguments: args });
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
export function robloxStudioClientConnected() {
|
|
111
|
+
return Boolean(client);
|
|
112
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { callRobloxStudioTool, disconnectRobloxStudioClient, listRobloxStudioTools, resolveRobloxStudioMcpLaunch, robloxStudioClientConnected, } from "./roblox-studio-client.js";
|
|
2
|
+
/** Official Studio MCP tools documented by Roblox (names as exposed by Studio). */
|
|
3
|
+
export const ROBLOX_OFFICIAL_TOOL_NAMES = [
|
|
4
|
+
"script_read",
|
|
5
|
+
"multi_edit",
|
|
6
|
+
"script_search",
|
|
7
|
+
"script_grep",
|
|
8
|
+
"generate_mesh",
|
|
9
|
+
"generate_material",
|
|
10
|
+
"generate_procedural_model",
|
|
11
|
+
"wait_job_finished",
|
|
12
|
+
"search_asset",
|
|
13
|
+
"insert_asset",
|
|
14
|
+
"upload_image",
|
|
15
|
+
"store_image",
|
|
16
|
+
"subagent",
|
|
17
|
+
"search_game_tree",
|
|
18
|
+
"inspect_instance",
|
|
19
|
+
"execute_luau",
|
|
20
|
+
"get_studio_state",
|
|
21
|
+
"start_stop_play",
|
|
22
|
+
"get_console_output",
|
|
23
|
+
"screen_capture",
|
|
24
|
+
"character_navigation",
|
|
25
|
+
"user_keyboard_input",
|
|
26
|
+
"user_mouse_input",
|
|
27
|
+
"http_get",
|
|
28
|
+
"skill",
|
|
29
|
+
"list_roblox_studios",
|
|
30
|
+
"set_active_studio",
|
|
31
|
+
];
|
|
32
|
+
export function auvryntRobloxToolName(remoteName) {
|
|
33
|
+
return remoteName.startsWith("roblox_") ? remoteName : `roblox_${remoteName}`;
|
|
34
|
+
}
|
|
35
|
+
export function remoteRobloxToolName(auvryntName) {
|
|
36
|
+
return auvryntName.startsWith("roblox_") ? auvryntName.slice("roblox_".length) : auvryntName;
|
|
37
|
+
}
|
|
38
|
+
export async function robloxStatus() {
|
|
39
|
+
const launch = resolveRobloxStudioMcpLaunch();
|
|
40
|
+
const base = {
|
|
41
|
+
integration: "roblox",
|
|
42
|
+
mode: "official-studio-mcp",
|
|
43
|
+
platform: launch.platform,
|
|
44
|
+
launcher: {
|
|
45
|
+
command: launch.command,
|
|
46
|
+
args: launch.args,
|
|
47
|
+
path: launch.hint,
|
|
48
|
+
exists: launch.pathExists,
|
|
49
|
+
},
|
|
50
|
+
connected: robloxStudioClientConnected(),
|
|
51
|
+
setup: [
|
|
52
|
+
"Open Roblox Studio",
|
|
53
|
+
"Assistant → … → Manage MCP Servers → Enable Studio as MCP server",
|
|
54
|
+
"Start Auvrynt with: auvrynt start roblox",
|
|
55
|
+
"Keep Studio open while using roblox_* tools",
|
|
56
|
+
],
|
|
57
|
+
docs: "https://create.roblox.com/docs/studio/mcp",
|
|
58
|
+
};
|
|
59
|
+
if (!launch.pathExists) {
|
|
60
|
+
return {
|
|
61
|
+
...base,
|
|
62
|
+
ok: false,
|
|
63
|
+
error: `Studio MCP launcher not found at ${launch.hint}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const tools = await listRobloxStudioTools(true);
|
|
68
|
+
return {
|
|
69
|
+
...base,
|
|
70
|
+
ok: true,
|
|
71
|
+
connected: true,
|
|
72
|
+
toolCount: tools.length,
|
|
73
|
+
tools: tools.map((t) => t.name),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
78
|
+
return {
|
|
79
|
+
...base,
|
|
80
|
+
ok: false,
|
|
81
|
+
connected: false,
|
|
82
|
+
error: message,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function robloxListTools(forceRefresh = false) {
|
|
87
|
+
const tools = await listRobloxStudioTools(forceRefresh);
|
|
88
|
+
return {
|
|
89
|
+
ok: true,
|
|
90
|
+
count: tools.length,
|
|
91
|
+
tools: tools.map((tool) => ({
|
|
92
|
+
remoteName: tool.name,
|
|
93
|
+
auvryntName: auvryntRobloxToolName(tool.name),
|
|
94
|
+
description: tool.description,
|
|
95
|
+
inputSchema: tool.inputSchema,
|
|
96
|
+
})),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export async function robloxCall(toolName, args = {}) {
|
|
100
|
+
const remote = remoteRobloxToolName(toolName);
|
|
101
|
+
return callRobloxStudioTool(remote, args);
|
|
102
|
+
}
|
|
103
|
+
export async function robloxDisconnect() {
|
|
104
|
+
await disconnectRobloxStudioClient();
|
|
105
|
+
return { ok: true };
|
|
106
|
+
}
|