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,42 @@
|
|
|
1
|
+
import { requestIp } from "../logger.js";
|
|
2
|
+
/** Small in-memory limiter for Auvrynt's single-process OAuth endpoints. */
|
|
3
|
+
export function createFixedWindowRateLimiter(options) {
|
|
4
|
+
const maxClients = options.maxClients ?? 10_000;
|
|
5
|
+
const buckets = new Map();
|
|
6
|
+
let requestCounter = 0;
|
|
7
|
+
const prune = (now) => {
|
|
8
|
+
for (const [key, bucket] of buckets) {
|
|
9
|
+
if (bucket.resetAt <= now)
|
|
10
|
+
buckets.delete(key);
|
|
11
|
+
}
|
|
12
|
+
while (buckets.size >= maxClients) {
|
|
13
|
+
const oldestKey = buckets.keys().next().value;
|
|
14
|
+
if (!oldestKey)
|
|
15
|
+
break;
|
|
16
|
+
buckets.delete(oldestKey);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
return (req, res, next) => {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
requestCounter++;
|
|
22
|
+
if (requestCounter % 100 === 0 || buckets.size >= maxClients)
|
|
23
|
+
prune(now);
|
|
24
|
+
const key = requestIp(req) ?? "unknown";
|
|
25
|
+
let bucket = buckets.get(key);
|
|
26
|
+
if (!bucket || bucket.resetAt <= now) {
|
|
27
|
+
if (buckets.size >= maxClients)
|
|
28
|
+
prune(now);
|
|
29
|
+
bucket = { count: 0, resetAt: now + options.windowMs };
|
|
30
|
+
buckets.set(key, bucket);
|
|
31
|
+
}
|
|
32
|
+
if (bucket.count >= options.maxRequests) {
|
|
33
|
+
const retryAfterSeconds = Math.max(1, Math.ceil((bucket.resetAt - now) / 1_000));
|
|
34
|
+
res.setHeader("Cache-Control", "no-store");
|
|
35
|
+
res.setHeader("Retry-After", String(retryAfterSeconds));
|
|
36
|
+
res.status(429).json({ error: options.message ?? "Too many requests. Try again later." });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
bucket.count++;
|
|
40
|
+
next();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const HTTP_REQUEST_TIMEOUT_MS = 5 * 60_000;
|
|
2
|
+
export const HTTP_HEADERS_TIMEOUT_MS = 30_000;
|
|
3
|
+
export const HTTP_KEEP_ALIVE_TIMEOUT_MS = 75_000;
|
|
4
|
+
export const HTTP_SOCKET_KEEP_ALIVE_DELAY_MS = 30_000;
|
|
5
|
+
export const HTTP_MAX_HEADERS_COUNT = 100;
|
|
6
|
+
export const HTTP_MAX_REQUESTS_PER_SOCKET = 10_000;
|
|
7
|
+
/**
|
|
8
|
+
* MCP responses may stream for a long time, but receiving request headers and
|
|
9
|
+
* bodies must still have finite bounds to resist slow-client DoS.
|
|
10
|
+
*/
|
|
11
|
+
export function hardenHttpServer(httpServer) {
|
|
12
|
+
httpServer.requestTimeout = HTTP_REQUEST_TIMEOUT_MS;
|
|
13
|
+
httpServer.headersTimeout = HTTP_HEADERS_TIMEOUT_MS;
|
|
14
|
+
httpServer.keepAliveTimeout = HTTP_KEEP_ALIVE_TIMEOUT_MS;
|
|
15
|
+
httpServer.timeout = 0;
|
|
16
|
+
httpServer.maxHeadersCount = HTTP_MAX_HEADERS_COUNT;
|
|
17
|
+
httpServer.maxRequestsPerSocket = HTTP_MAX_REQUESTS_PER_SOCKET;
|
|
18
|
+
httpServer.on("connection", configureHttpSocket);
|
|
19
|
+
}
|
|
20
|
+
export function configureHttpSocket(socket) {
|
|
21
|
+
socket.setKeepAlive(true, HTTP_SOCKET_KEEP_ALIVE_DELAY_MS);
|
|
22
|
+
socket.setNoDelay(true);
|
|
23
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const queues = new Map();
|
|
2
|
+
export const MAX_INTEGRATION_QUEUE_DEPTH = 16;
|
|
3
|
+
export function enqueueIntegration(integration, fn) {
|
|
4
|
+
const state = queues.get(integration) ?? {
|
|
5
|
+
tail: Promise.resolve(),
|
|
6
|
+
pending: 0,
|
|
7
|
+
active: false,
|
|
8
|
+
};
|
|
9
|
+
const depth = state.pending + (state.active ? 1 : 0);
|
|
10
|
+
if (depth >= MAX_INTEGRATION_QUEUE_DEPTH) {
|
|
11
|
+
throw new Error(`${integration} integration queue is full (max ${MAX_INTEGRATION_QUEUE_DEPTH}). Wait for the active operation before retrying.`);
|
|
12
|
+
}
|
|
13
|
+
state.pending += 1;
|
|
14
|
+
queues.set(integration, state);
|
|
15
|
+
const previous = state.tail.catch(() => undefined);
|
|
16
|
+
let release;
|
|
17
|
+
state.tail = new Promise((resolve) => { release = resolve; });
|
|
18
|
+
return previous.then(async () => {
|
|
19
|
+
state.pending -= 1;
|
|
20
|
+
state.active = true;
|
|
21
|
+
try {
|
|
22
|
+
return await fn();
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
state.active = false;
|
|
26
|
+
release();
|
|
27
|
+
if (state.pending === 0 && queues.get(integration) === state) {
|
|
28
|
+
queues.delete(integration);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function getQueueDiagnostics() {
|
|
34
|
+
const result = {};
|
|
35
|
+
for (const [integration, state] of queues) {
|
|
36
|
+
result[integration] = { active: state.active, pending: state.pending };
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
export function clearIntegrationQueue(integration) {
|
|
41
|
+
const state = queues.get(integration);
|
|
42
|
+
if (state?.active || (state?.pending ?? 0) > 0) {
|
|
43
|
+
throw new Error(`Cannot clear active integration queue: ${integration}`);
|
|
44
|
+
}
|
|
45
|
+
queues.delete(integration);
|
|
46
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const MAX_RECENT_LOG_ENTRIES = 500;
|
|
2
|
+
const recentEntries = [];
|
|
3
|
+
let nextRecentLogId = 1;
|
|
4
|
+
const LEVEL_WEIGHT = {
|
|
5
|
+
silent: 0,
|
|
6
|
+
error: 1,
|
|
7
|
+
warn: 2,
|
|
8
|
+
info: 3,
|
|
9
|
+
debug: 4,
|
|
10
|
+
};
|
|
11
|
+
export function shouldLog(config, level) {
|
|
12
|
+
return LEVEL_WEIGHT[config.level] >= LEVEL_WEIGHT[level];
|
|
13
|
+
}
|
|
14
|
+
export function logEvent(config, level, event, fields = {}) {
|
|
15
|
+
if (!shouldLog(config, level))
|
|
16
|
+
return;
|
|
17
|
+
const ts = new Date().toISOString();
|
|
18
|
+
const safeFields = sanitizeLogFields(fields);
|
|
19
|
+
recentEntries.push({ id: nextRecentLogId++, ts, level, event, fields: safeFields });
|
|
20
|
+
if (recentEntries.length > MAX_RECENT_LOG_ENTRIES) {
|
|
21
|
+
recentEntries.splice(0, recentEntries.length - MAX_RECENT_LOG_ENTRIES);
|
|
22
|
+
}
|
|
23
|
+
if (process.env.AUVRYNT_START_MODE === "true") {
|
|
24
|
+
const emitter = global.auvryntLogEmitter;
|
|
25
|
+
if (typeof emitter === "function") {
|
|
26
|
+
emitter(level, event, safeFields);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const entry = {
|
|
31
|
+
ts,
|
|
32
|
+
level,
|
|
33
|
+
event,
|
|
34
|
+
...safeFields,
|
|
35
|
+
};
|
|
36
|
+
const line = config.format === "pretty" ? formatPretty(entry) : JSON.stringify(entry);
|
|
37
|
+
if (level === "error") {
|
|
38
|
+
console.error(line);
|
|
39
|
+
}
|
|
40
|
+
else if (level === "warn") {
|
|
41
|
+
console.warn(line);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.log(line);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function recentLogEntries(limit = 200) {
|
|
48
|
+
const boundedLimit = Number.isInteger(limit)
|
|
49
|
+
? Math.max(1, Math.min(limit, MAX_RECENT_LOG_ENTRIES))
|
|
50
|
+
: 200;
|
|
51
|
+
return recentEntries.slice(-boundedLimit).map((entry) => ({
|
|
52
|
+
...entry,
|
|
53
|
+
fields: { ...entry.fields },
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
export function requestIp(req) {
|
|
57
|
+
// Express has already applied the server's trusted-proxy policy to req.ip.
|
|
58
|
+
// Reading forwarding headers directly here would let callers spoof log identities.
|
|
59
|
+
return req.ip ?? req.socket.remoteAddress;
|
|
60
|
+
}
|
|
61
|
+
export function isLoopbackIp(value) {
|
|
62
|
+
if (!value)
|
|
63
|
+
return false;
|
|
64
|
+
const normalized = value.trim().toLowerCase();
|
|
65
|
+
return normalized === "127.0.0.1"
|
|
66
|
+
|| normalized === "::1"
|
|
67
|
+
|| normalized === "localhost"
|
|
68
|
+
|| normalized.startsWith("127.")
|
|
69
|
+
|| normalized.startsWith("::ffff:127.");
|
|
70
|
+
}
|
|
71
|
+
export function isLoopbackRequest(req) {
|
|
72
|
+
return isLoopbackIp(requestIp(req));
|
|
73
|
+
}
|
|
74
|
+
export function requestPath(req) {
|
|
75
|
+
return req.path || req.url.split("?")[0] || req.url;
|
|
76
|
+
}
|
|
77
|
+
export function sessionIdPrefix(sessionId) {
|
|
78
|
+
return sessionId ? sessionId.slice(0, 8) : undefined;
|
|
79
|
+
}
|
|
80
|
+
export function commandPreview(command) {
|
|
81
|
+
const normalized = command.replace(/\s+/g, " ").trim();
|
|
82
|
+
return normalized.length > 120 ? `${normalized.slice(0, 117)}...` : normalized;
|
|
83
|
+
}
|
|
84
|
+
function sanitizeLogFields(fields) {
|
|
85
|
+
return Object.fromEntries(Object.entries(fields).map(([key, value]) => [key, sanitizeLogValue(key, value)]));
|
|
86
|
+
}
|
|
87
|
+
function sanitizeLogValue(key, value) {
|
|
88
|
+
if (/authorization|token|secret|password|cookie/i.test(key))
|
|
89
|
+
return "[redacted]";
|
|
90
|
+
if (/(^|_)(ip|address)$/i.test(key) || /remote(address)?/i.test(key))
|
|
91
|
+
return "[network-address]";
|
|
92
|
+
if (typeof value === "string") {
|
|
93
|
+
const redacted = redactNetworkAddresses(value);
|
|
94
|
+
return redacted.length > 2_000 ? `${redacted.slice(0, 1_997)}...` : redacted;
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
return value.slice(0, 50).map((item) => sanitizeLogValue("item", item));
|
|
98
|
+
}
|
|
99
|
+
if (value && typeof value === "object") {
|
|
100
|
+
return Object.fromEntries(Object.entries(value)
|
|
101
|
+
.slice(0, 50)
|
|
102
|
+
.map(([childKey, childValue]) => [
|
|
103
|
+
childKey,
|
|
104
|
+
sanitizeLogValue(childKey, childValue),
|
|
105
|
+
]));
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
function redactNetworkAddresses(value) {
|
|
110
|
+
return value
|
|
111
|
+
.replace(/\b(?:\d{1,3}\.){3}\d{1,3}(?::\d+)?\b/g, "[network-address]")
|
|
112
|
+
.replace(/\[(?:[0-9a-f]*:){2,}[0-9a-f:]*\](?::\d+)?/gi, "[network-address]")
|
|
113
|
+
.replace(/\b::1\b/gi, "[network-address]");
|
|
114
|
+
}
|
|
115
|
+
function formatPretty(entry) {
|
|
116
|
+
const ts = String(entry.ts);
|
|
117
|
+
const level = String(entry.level).toUpperCase();
|
|
118
|
+
const event = String(entry.event);
|
|
119
|
+
const rest = Object.entries(entry)
|
|
120
|
+
.filter(([key, value]) => !["ts", "level", "event"].includes(key) && value !== undefined)
|
|
121
|
+
.map(([key, value]) => `${key}=${formatPrettyValue(value)}`)
|
|
122
|
+
.join(" ");
|
|
123
|
+
return rest ? `${ts} ${level} ${event} ${rest}` : `${ts} ${level} ${event}`;
|
|
124
|
+
}
|
|
125
|
+
function formatPrettyValue(value) {
|
|
126
|
+
if (typeof value === "string")
|
|
127
|
+
return JSON.stringify(value);
|
|
128
|
+
return JSON.stringify(value);
|
|
129
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
export const DEFAULT_MCP_EVENT_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
3
|
+
export const DEFAULT_MCP_EVENT_LIMIT = 2_048;
|
|
4
|
+
export const DEFAULT_MCP_EVENT_STORE_BYTES = 3 * 1024 * 1024;
|
|
5
|
+
export const DEFAULT_MCP_SINGLE_EVENT_BYTES = 1024 * 1024;
|
|
6
|
+
/**
|
|
7
|
+
* Per-session replay storage for MCP SSE reconnects. It is deliberately bounded
|
|
8
|
+
* by age and count so a long-running web-agent connection cannot grow memory
|
|
9
|
+
* without limit.
|
|
10
|
+
*/
|
|
11
|
+
export class BoundedMcpEventStore {
|
|
12
|
+
events = [];
|
|
13
|
+
eventsById = new Map();
|
|
14
|
+
maxEvents;
|
|
15
|
+
maxBytes;
|
|
16
|
+
maxEventBytes;
|
|
17
|
+
retentionMs;
|
|
18
|
+
now;
|
|
19
|
+
totalBytes = 0;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.maxEvents = positiveInteger(options.maxEvents, DEFAULT_MCP_EVENT_LIMIT);
|
|
22
|
+
this.maxBytes = positiveInteger(options.maxBytes, DEFAULT_MCP_EVENT_STORE_BYTES);
|
|
23
|
+
this.maxEventBytes = Math.min(positiveInteger(options.maxEventBytes, DEFAULT_MCP_SINGLE_EVENT_BYTES), this.maxBytes);
|
|
24
|
+
this.retentionMs = positiveInteger(options.retentionMs, DEFAULT_MCP_EVENT_RETENTION_MS);
|
|
25
|
+
this.now = options.now ?? Date.now;
|
|
26
|
+
}
|
|
27
|
+
async storeEvent(streamId, message) {
|
|
28
|
+
const storedAt = this.now();
|
|
29
|
+
this.prune(storedAt);
|
|
30
|
+
const eventId = `evt-${storedAt.toString(36)}-${randomUUID()}`;
|
|
31
|
+
const measuredBytes = serializedBytes(message);
|
|
32
|
+
const replayMessage = measuredBytes > this.maxEventBytes
|
|
33
|
+
? replayTooLargeMessage(message, measuredBytes, this.maxEventBytes)
|
|
34
|
+
: message;
|
|
35
|
+
const bytes = serializedBytes(replayMessage);
|
|
36
|
+
const event = { eventId, streamId, message: replayMessage, storedAt, bytes };
|
|
37
|
+
this.events.push(event);
|
|
38
|
+
this.eventsById.set(eventId, event);
|
|
39
|
+
this.totalBytes += bytes;
|
|
40
|
+
this.prune(storedAt);
|
|
41
|
+
return eventId;
|
|
42
|
+
}
|
|
43
|
+
async getStreamIdForEventId(eventId) {
|
|
44
|
+
this.prune(this.now());
|
|
45
|
+
return this.eventsById.get(eventId)?.streamId;
|
|
46
|
+
}
|
|
47
|
+
async replayEventsAfter(lastEventId, options) {
|
|
48
|
+
this.prune(this.now());
|
|
49
|
+
const lastEvent = this.eventsById.get(lastEventId);
|
|
50
|
+
if (!lastEvent)
|
|
51
|
+
return "";
|
|
52
|
+
let found = false;
|
|
53
|
+
for (const event of this.events) {
|
|
54
|
+
if (event.streamId !== lastEvent.streamId)
|
|
55
|
+
continue;
|
|
56
|
+
if (event.eventId === lastEventId) {
|
|
57
|
+
found = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (found)
|
|
61
|
+
await options.send(event.eventId, event.message);
|
|
62
|
+
}
|
|
63
|
+
return lastEvent.streamId;
|
|
64
|
+
}
|
|
65
|
+
size() {
|
|
66
|
+
this.prune(this.now());
|
|
67
|
+
return this.events.length;
|
|
68
|
+
}
|
|
69
|
+
byteSize() {
|
|
70
|
+
this.prune(this.now());
|
|
71
|
+
return this.totalBytes;
|
|
72
|
+
}
|
|
73
|
+
prune(now) {
|
|
74
|
+
const oldestAllowed = now - this.retentionMs;
|
|
75
|
+
while (this.events.length > 0
|
|
76
|
+
&& (this.events[0].storedAt < oldestAllowed
|
|
77
|
+
|| this.events.length > this.maxEvents
|
|
78
|
+
|| this.totalBytes > this.maxBytes)) {
|
|
79
|
+
const removed = this.events.shift();
|
|
80
|
+
if (removed) {
|
|
81
|
+
this.eventsById.delete(removed.eventId);
|
|
82
|
+
this.totalBytes = Math.max(0, this.totalBytes - removed.bytes);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function positiveInteger(value, fallback) {
|
|
88
|
+
return Number.isInteger(value) && value > 0 ? value : fallback;
|
|
89
|
+
}
|
|
90
|
+
function serializedBytes(message) {
|
|
91
|
+
return Buffer.byteLength(JSON.stringify(message), "utf8");
|
|
92
|
+
}
|
|
93
|
+
function replayTooLargeMessage(message, actualBytes, limitBytes) {
|
|
94
|
+
const id = "id" in message ? message.id : null;
|
|
95
|
+
return {
|
|
96
|
+
jsonrpc: "2.0",
|
|
97
|
+
id,
|
|
98
|
+
error: {
|
|
99
|
+
code: -32002,
|
|
100
|
+
message: `The original ${actualBytes}-byte result exceeded the ${limitBytes}-byte replay limit. Retry with a narrower request.`,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { loadAuvryntFiles, writeAuvryntAuth, writeAuvryntConfig, } from "./user-config.js";
|
|
3
|
+
export const MAX_NGROK_AUTHTOKENS = 20;
|
|
4
|
+
export function validateNgrokAuthtoken(value) {
|
|
5
|
+
if (typeof value !== "string")
|
|
6
|
+
throw new Error("Enter a valid ngrok authtoken.");
|
|
7
|
+
const token = value.trim();
|
|
8
|
+
if (token.length < 16 || token.length > 512) {
|
|
9
|
+
throw new Error("ngrok authtokens must be between 16 and 512 characters.");
|
|
10
|
+
}
|
|
11
|
+
if (/\s|[\u0000-\u001f\u007f]/u.test(token)) {
|
|
12
|
+
throw new Error("ngrok authtokens must not contain whitespace or control characters.");
|
|
13
|
+
}
|
|
14
|
+
return token;
|
|
15
|
+
}
|
|
16
|
+
export function ngrokAuthtokenFingerprint(token) {
|
|
17
|
+
return createHash("sha256").update(token).digest("hex").slice(0, 10).toUpperCase();
|
|
18
|
+
}
|
|
19
|
+
export function resolveNgrokAuthtoken(files = loadAuvryntFiles(), env = process.env) {
|
|
20
|
+
const override = env.AUVRYNT_NGROK_AUTHTOKEN?.trim();
|
|
21
|
+
if (override)
|
|
22
|
+
return override;
|
|
23
|
+
const pool = readPool(files, env);
|
|
24
|
+
return pool.activeIndex === null ? undefined : pool.tokens[pool.activeIndex];
|
|
25
|
+
}
|
|
26
|
+
export function getCurrentNgrokAuthtoken(files = loadAuvryntFiles(), env = process.env) {
|
|
27
|
+
return resolveNgrokAuthtoken(files, env);
|
|
28
|
+
}
|
|
29
|
+
export function summarizeNgrokAuthtokens(files = loadAuvryntFiles(), env = process.env) {
|
|
30
|
+
return summarizePool(readPool(files, env));
|
|
31
|
+
}
|
|
32
|
+
export function addNgrokAuthtoken(value, env = process.env) {
|
|
33
|
+
const token = validateNgrokAuthtoken(value);
|
|
34
|
+
const files = loadAuvryntFiles(env);
|
|
35
|
+
const pool = readPool(files, env);
|
|
36
|
+
if (pool.tokens.includes(token))
|
|
37
|
+
throw new Error("That ngrok authtoken is already saved.");
|
|
38
|
+
if (pool.tokens.length >= MAX_NGROK_AUTHTOKENS) {
|
|
39
|
+
throw new Error(`A maximum of ${MAX_NGROK_AUTHTOKENS} ngrok authtokens can be saved.`);
|
|
40
|
+
}
|
|
41
|
+
const previousActiveToken = activeToken(pool);
|
|
42
|
+
pool.tokens.push(token);
|
|
43
|
+
if (pool.activeIndex === null || activeTokenIsExhausted(pool)) {
|
|
44
|
+
pool.activeIndex = firstAvailableTokenIndex(pool) ?? pool.tokens.length - 1;
|
|
45
|
+
}
|
|
46
|
+
const activeChanged = previousActiveToken !== activeToken(pool);
|
|
47
|
+
persistPool(files, pool, env);
|
|
48
|
+
return { summary: summarizePool(pool), activeChanged };
|
|
49
|
+
}
|
|
50
|
+
export function removeNgrokAuthtoken(index, env = process.env) {
|
|
51
|
+
const files = loadAuvryntFiles(env);
|
|
52
|
+
const pool = readPool(files, env);
|
|
53
|
+
const tokenIndex = parseTokenIndex(index, pool.tokens.length);
|
|
54
|
+
const previousActive = pool.activeIndex;
|
|
55
|
+
const previousActiveToken = activeToken(pool);
|
|
56
|
+
const [removed] = pool.tokens.splice(tokenIndex, 1);
|
|
57
|
+
if (removed)
|
|
58
|
+
delete pool.quotaExhausted[ngrokAuthtokenFingerprint(removed)];
|
|
59
|
+
if (pool.tokens.length === 0) {
|
|
60
|
+
pool.activeIndex = null;
|
|
61
|
+
}
|
|
62
|
+
else if (previousActive === null) {
|
|
63
|
+
pool.activeIndex = 0;
|
|
64
|
+
}
|
|
65
|
+
else if (tokenIndex < previousActive) {
|
|
66
|
+
pool.activeIndex = previousActive - 1;
|
|
67
|
+
}
|
|
68
|
+
else if (tokenIndex === previousActive) {
|
|
69
|
+
pool.activeIndex = Math.min(tokenIndex, pool.tokens.length - 1);
|
|
70
|
+
}
|
|
71
|
+
if (activeTokenIsExhausted(pool)) {
|
|
72
|
+
pool.activeIndex = firstAvailableTokenIndex(pool) ?? pool.activeIndex;
|
|
73
|
+
}
|
|
74
|
+
const activeChanged = previousActiveToken !== activeToken(pool);
|
|
75
|
+
persistPool(files, pool, env);
|
|
76
|
+
return { summary: summarizePool(pool), activeChanged };
|
|
77
|
+
}
|
|
78
|
+
export function activateNgrokAuthtoken(index, env = process.env) {
|
|
79
|
+
if (env.AUVRYNT_NGROK_AUTHTOKEN?.trim()) {
|
|
80
|
+
throw new Error("AUVRYNT_NGROK_AUTHTOKEN is overriding the saved token pool. Remove that environment variable before switching tokens here.");
|
|
81
|
+
}
|
|
82
|
+
const files = loadAuvryntFiles(env);
|
|
83
|
+
const pool = readPool(files, env);
|
|
84
|
+
const tokenIndex = parseTokenIndex(index, pool.tokens.length);
|
|
85
|
+
const fingerprint = ngrokAuthtokenFingerprint(pool.tokens[tokenIndex]);
|
|
86
|
+
const activeChanged = pool.activeIndex !== tokenIndex || Boolean(pool.quotaExhausted[fingerprint]);
|
|
87
|
+
pool.activeIndex = tokenIndex;
|
|
88
|
+
delete pool.quotaExhausted[fingerprint];
|
|
89
|
+
persistPool(files, pool, env);
|
|
90
|
+
return { summary: summarizePool(pool), activeChanged };
|
|
91
|
+
}
|
|
92
|
+
export function markActiveNgrokAuthtokenQuotaExceeded(exhaustedAt = new Date().toISOString(), env = process.env) {
|
|
93
|
+
const files = loadAuvryntFiles(env);
|
|
94
|
+
const pool = readPool(files, env);
|
|
95
|
+
if (pool.environmentOverride) {
|
|
96
|
+
return {
|
|
97
|
+
environmentOverride: true,
|
|
98
|
+
summary: summarizePool(pool),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (pool.activeIndex === null || pool.tokens.length === 0) {
|
|
102
|
+
return {
|
|
103
|
+
environmentOverride: false,
|
|
104
|
+
summary: summarizePool(pool),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const currentIndex = pool.activeIndex;
|
|
108
|
+
const currentToken = pool.tokens[currentIndex];
|
|
109
|
+
const currentFingerprint = ngrokAuthtokenFingerprint(currentToken);
|
|
110
|
+
pool.quotaExhausted[currentFingerprint] = exhaustedAt;
|
|
111
|
+
let nextIndex = null;
|
|
112
|
+
for (let offset = 1; offset < pool.tokens.length; offset++) {
|
|
113
|
+
const candidateIndex = (currentIndex + offset) % pool.tokens.length;
|
|
114
|
+
const candidateFingerprint = ngrokAuthtokenFingerprint(pool.tokens[candidateIndex]);
|
|
115
|
+
if (!pool.quotaExhausted[candidateFingerprint]) {
|
|
116
|
+
nextIndex = candidateIndex;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (nextIndex !== null)
|
|
121
|
+
pool.activeIndex = nextIndex;
|
|
122
|
+
persistPool(files, pool, env);
|
|
123
|
+
const summary = summarizePool(pool);
|
|
124
|
+
return {
|
|
125
|
+
environmentOverride: false,
|
|
126
|
+
current: summary.tokens.find((token) => token.fingerprint === currentFingerprint),
|
|
127
|
+
next: nextIndex === null ? undefined : summary.tokens.find((token) => token.index === nextIndex),
|
|
128
|
+
summary,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function readPool(files, env) {
|
|
132
|
+
const environmentOverride = Boolean(env.AUVRYNT_NGROK_AUTHTOKEN?.trim());
|
|
133
|
+
const saved = Array.isArray(files.auth.ngrokAuthtokens)
|
|
134
|
+
? files.auth.ngrokAuthtokens.map((token) => token.trim()).filter(Boolean)
|
|
135
|
+
: [];
|
|
136
|
+
const legacy = files.config.ngrokAuthtoken?.trim();
|
|
137
|
+
const tokens = deduplicate(legacy ? [...saved, legacy] : saved);
|
|
138
|
+
const configuredIndex = files.auth.ngrokActiveAuthtokenIndex;
|
|
139
|
+
const activeIndex = tokens.length === 0
|
|
140
|
+
? null
|
|
141
|
+
: Number.isInteger(configuredIndex) && configuredIndex >= 0 && configuredIndex < tokens.length
|
|
142
|
+
? configuredIndex
|
|
143
|
+
: 0;
|
|
144
|
+
return {
|
|
145
|
+
tokens,
|
|
146
|
+
activeIndex,
|
|
147
|
+
quotaExhausted: { ...(files.auth.ngrokQuotaExhausted ?? {}) },
|
|
148
|
+
environmentOverride,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function persistPool(files, pool, env) {
|
|
152
|
+
const validFingerprints = new Set(pool.tokens.map(ngrokAuthtokenFingerprint));
|
|
153
|
+
const quotaExhausted = Object.fromEntries(Object.entries(pool.quotaExhausted).filter(([fingerprint]) => validFingerprints.has(fingerprint)));
|
|
154
|
+
const nextAuth = {
|
|
155
|
+
...files.auth,
|
|
156
|
+
ngrokAuthtokens: pool.tokens,
|
|
157
|
+
ngrokQuotaExhausted: quotaExhausted,
|
|
158
|
+
};
|
|
159
|
+
if (pool.activeIndex === null)
|
|
160
|
+
delete nextAuth.ngrokActiveAuthtokenIndex;
|
|
161
|
+
else
|
|
162
|
+
nextAuth.ngrokActiveAuthtokenIndex = pool.activeIndex;
|
|
163
|
+
writeAuvryntAuth(nextAuth, env);
|
|
164
|
+
if (files.config.ngrokAuthtoken !== undefined) {
|
|
165
|
+
const nextConfig = { ...files.config };
|
|
166
|
+
delete nextConfig.ngrokAuthtoken;
|
|
167
|
+
writeAuvryntConfig(nextConfig, env);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function summarizePool(pool) {
|
|
171
|
+
return {
|
|
172
|
+
enabled: pool.tokens.length > 0,
|
|
173
|
+
environmentOverride: pool.environmentOverride,
|
|
174
|
+
activeIndex: pool.environmentOverride ? null : pool.activeIndex,
|
|
175
|
+
tokens: pool.tokens.map((token, index) => {
|
|
176
|
+
const fingerprint = ngrokAuthtokenFingerprint(token);
|
|
177
|
+
return {
|
|
178
|
+
index,
|
|
179
|
+
fingerprint,
|
|
180
|
+
active: !pool.environmentOverride && index === pool.activeIndex,
|
|
181
|
+
...(pool.quotaExhausted[fingerprint]
|
|
182
|
+
? { quotaExhaustedAt: pool.quotaExhausted[fingerprint] }
|
|
183
|
+
: {}),
|
|
184
|
+
};
|
|
185
|
+
}),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function activeToken(pool) {
|
|
189
|
+
return pool.activeIndex === null ? undefined : pool.tokens[pool.activeIndex];
|
|
190
|
+
}
|
|
191
|
+
function activeTokenIsExhausted(pool) {
|
|
192
|
+
const token = activeToken(pool);
|
|
193
|
+
return Boolean(token && pool.quotaExhausted[ngrokAuthtokenFingerprint(token)]);
|
|
194
|
+
}
|
|
195
|
+
function firstAvailableTokenIndex(pool) {
|
|
196
|
+
const index = pool.tokens.findIndex((token) => !pool.quotaExhausted[ngrokAuthtokenFingerprint(token)]);
|
|
197
|
+
return index >= 0 ? index : null;
|
|
198
|
+
}
|
|
199
|
+
function parseTokenIndex(value, length) {
|
|
200
|
+
const index = Number(value);
|
|
201
|
+
if (!Number.isInteger(index) || index < 0 || index >= length) {
|
|
202
|
+
throw new Error("Select a valid saved ngrok token.");
|
|
203
|
+
}
|
|
204
|
+
return index;
|
|
205
|
+
}
|
|
206
|
+
function deduplicate(tokens) {
|
|
207
|
+
return Array.from(new Set(tokens));
|
|
208
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
const als = new AsyncLocalStorage();
|
|
3
|
+
export function runWithContext(ctx, fn) {
|
|
4
|
+
return als.run(ctx, fn);
|
|
5
|
+
}
|
|
6
|
+
export function getRequestContext() {
|
|
7
|
+
return als.getStore();
|
|
8
|
+
}
|
|
9
|
+
export function requireRequestContext() {
|
|
10
|
+
const ctx = als.getStore();
|
|
11
|
+
if (!ctx)
|
|
12
|
+
throw new Error("No request context available");
|
|
13
|
+
return ctx;
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { disconnectAllGodotEditorBridges } from "../godot-editor-bridge.js";
|
|
2
|
+
import { clearBlenderClients } from "../blender-client.js";
|
|
3
|
+
import { logEvent } from "../logger.js";
|
|
4
|
+
export function createShutdownCoordinator(config, sessionRegistry, processManager, serenaManager, workspaceStore) {
|
|
5
|
+
let phase = "none";
|
|
6
|
+
let shutdownPromise;
|
|
7
|
+
return {
|
|
8
|
+
get phase() {
|
|
9
|
+
return phase;
|
|
10
|
+
},
|
|
11
|
+
shutdown() {
|
|
12
|
+
if (shutdownPromise)
|
|
13
|
+
return shutdownPromise;
|
|
14
|
+
shutdownPromise = (async () => {
|
|
15
|
+
const log = (msg, p) => {
|
|
16
|
+
phase = p;
|
|
17
|
+
logEvent(config.logging, "info", "shutdown", { phase: p, message: msg });
|
|
18
|
+
};
|
|
19
|
+
log("Closing MCP sessions...", "sessions_closed");
|
|
20
|
+
await sessionRegistry.closeAll("server_shutdown");
|
|
21
|
+
sessionRegistry.close();
|
|
22
|
+
log("Terminating processes...", "processes_terminated");
|
|
23
|
+
await Promise.allSettled([
|
|
24
|
+
serenaManager.stopAllSessions(),
|
|
25
|
+
processManager.stopAllProcesses(),
|
|
26
|
+
]);
|
|
27
|
+
log("Disconnecting integrations...", "integrations_disconnected");
|
|
28
|
+
disconnectAllGodotEditorBridges();
|
|
29
|
+
clearBlenderClients();
|
|
30
|
+
log("Closing database...", "database_closed");
|
|
31
|
+
workspaceStore.close?.();
|
|
32
|
+
log("Shutdown complete", "database_closed");
|
|
33
|
+
})();
|
|
34
|
+
return shutdownPromise;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const DEFAULT_SSE_HEARTBEAT_INTERVAL_MS = 15_000;
|
|
2
|
+
/**
|
|
3
|
+
* Keeps long-running MCP SSE responses active through reverse proxies and
|
|
4
|
+
* tunnels. SSE comment frames are ignored by clients but count as traffic, so
|
|
5
|
+
* an otherwise silent build or render is less likely to be terminated as an
|
|
6
|
+
* upstream 502/504 timeout.
|
|
7
|
+
*/
|
|
8
|
+
export function attachSseHeartbeat(response, intervalMs = DEFAULT_SSE_HEARTBEAT_INTERVAL_MS) {
|
|
9
|
+
if (!Number.isFinite(intervalMs) || intervalMs < 1) {
|
|
10
|
+
throw new Error("SSE heartbeat interval must be a positive number.");
|
|
11
|
+
}
|
|
12
|
+
if (!response.headersSent) {
|
|
13
|
+
response.setHeader("Cache-Control", "no-cache, no-transform");
|
|
14
|
+
response.setHeader("X-Accel-Buffering", "no");
|
|
15
|
+
}
|
|
16
|
+
let stopped = false;
|
|
17
|
+
const stop = () => {
|
|
18
|
+
if (stopped)
|
|
19
|
+
return;
|
|
20
|
+
stopped = true;
|
|
21
|
+
clearInterval(timer);
|
|
22
|
+
};
|
|
23
|
+
const timer = setInterval(() => {
|
|
24
|
+
if (response.writableEnded || response.destroyed) {
|
|
25
|
+
stop();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!response.headersSent || !isEventStream(response.getHeader("content-type")))
|
|
29
|
+
return;
|
|
30
|
+
try {
|
|
31
|
+
response.write(`: auvrynt-heartbeat ${Date.now()}\n\n`);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
stop();
|
|
35
|
+
}
|
|
36
|
+
}, intervalMs);
|
|
37
|
+
timer.unref();
|
|
38
|
+
response.once("finish", stop);
|
|
39
|
+
response.once("close", stop);
|
|
40
|
+
return { stop };
|
|
41
|
+
}
|
|
42
|
+
function isEventStream(contentType) {
|
|
43
|
+
if (Array.isArray(contentType)) {
|
|
44
|
+
return contentType.some((value) => String(value).toLowerCase().includes("text/event-stream"));
|
|
45
|
+
}
|
|
46
|
+
return String(contentType ?? "").toLowerCase().includes("text/event-stream");
|
|
47
|
+
}
|