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,1509 @@
|
|
|
1
|
+
import { AUVRYNT_COMMANDS } from "../infrastructure/command-reference.js";
|
|
2
|
+
import { DASHBOARD_PAGE_CSS } from "./dashboard-page-styles.js";
|
|
3
|
+
const PAGE_META = {
|
|
4
|
+
analytics: {
|
|
5
|
+
kicker: "Operations",
|
|
6
|
+
title: "Analytics",
|
|
7
|
+
description: "",
|
|
8
|
+
},
|
|
9
|
+
connectivity: {
|
|
10
|
+
kicker: "Configuration",
|
|
11
|
+
title: "Connectivity",
|
|
12
|
+
description: "Manage MCP endpoints, the active workspace, and local integrations.",
|
|
13
|
+
},
|
|
14
|
+
secrets: {
|
|
15
|
+
kicker: "Private configuration",
|
|
16
|
+
title: "Secrets",
|
|
17
|
+
description: "Manage OS-protected local ngrok credentials and automatic quota failover.",
|
|
18
|
+
},
|
|
19
|
+
logs: {
|
|
20
|
+
kicker: "Observability",
|
|
21
|
+
title: "Logs",
|
|
22
|
+
description: "Inspect MCP, tool, authentication, and lifecycle events.",
|
|
23
|
+
},
|
|
24
|
+
commands: {
|
|
25
|
+
kicker: "Reference",
|
|
26
|
+
title: "Commands",
|
|
27
|
+
description: "Search the local Auvrynt command surface.",
|
|
28
|
+
},
|
|
29
|
+
tools: {
|
|
30
|
+
kicker: "Configuration",
|
|
31
|
+
title: "Tool settings",
|
|
32
|
+
description: "Enable or disable individual integration tools. Disabled tools disappear from the MCP tool list.",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
export function dashboardHtml(view, nonce) {
|
|
36
|
+
const commands = commandMarkup();
|
|
37
|
+
const initialData = jsonForHtml(view);
|
|
38
|
+
const firstRoot = view.allowedRoots[0] ?? "";
|
|
39
|
+
return `<!doctype html>
|
|
40
|
+
<html lang="en">
|
|
41
|
+
<head>
|
|
42
|
+
<meta charset="UTF-8" />
|
|
43
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
44
|
+
<title>Auvrynt Dashboard</title>
|
|
45
|
+
<style nonce="${escapeHtml(nonce)}">
|
|
46
|
+
${DASHBOARD_PAGE_CSS}
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<div class="app-shell">
|
|
51
|
+
<aside class="control-rail" aria-label="Auvrynt controls">
|
|
52
|
+
<div class="brand">
|
|
53
|
+
<img src="/brand-assets/auvrynt-icon.png" alt="" width="38" height="38" />
|
|
54
|
+
<div><span class="brand-name">Auvrynt</span><span class="brand-version">PID <span id="rail-pid">${view.pid}</span></span></div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<section class="agent-presence" id="agent-presence" data-state="${view.agentState}" aria-live="polite">
|
|
58
|
+
<div class="presence-head"><span class="presence-dot" aria-hidden="true"></span><strong class="presence-title" id="presence-title">${escapeHtml(agentStateLabel(view))}</strong></div>
|
|
59
|
+
<p class="presence-detail" id="presence-detail">${escapeHtml(agentStateDetail(view))}</p>
|
|
60
|
+
</section>
|
|
61
|
+
|
|
62
|
+
<p class="rail-label">Workspace</p>
|
|
63
|
+
<nav class="dashboard-nav" role="tablist" aria-label="Dashboard views" aria-orientation="vertical">
|
|
64
|
+
<button class="nav-tab" id="tab-analytics" type="button" role="tab" aria-selected="true" aria-controls="view-analytics" data-view="analytics"><span class="nav-icon">A</span><span class="nav-label">Analytics</span><span class="nav-count" id="nav-session-count">${view.sessions}/${view.maxSessions}</span></button>
|
|
65
|
+
<button class="nav-tab" id="tab-connectivity" type="button" role="tab" aria-selected="false" aria-controls="view-connectivity" data-view="connectivity"><span class="nav-icon">C</span><span class="nav-label">Connectivity</span><span class="nav-count">${view.integrations.filter((item) => item.enabled).length}</span></button>
|
|
66
|
+
<button class="nav-tab" id="tab-secrets" type="button" role="tab" aria-selected="false" aria-controls="view-secrets" data-view="secrets"><span class="nav-icon">S</span><span class="nav-label">Secrets</span><span class="nav-count" id="nav-token-count">${view.ngrok.tokens.length}</span></button>
|
|
67
|
+
<button class="nav-tab" id="tab-logs" type="button" role="tab" aria-selected="false" aria-controls="view-logs" data-view="logs"><span class="nav-icon">L</span><span class="nav-label">Logs</span><span class="nav-count" id="nav-log-count">${view.logs.length}</span></button>
|
|
68
|
+
<button class="nav-tab" id="tab-commands" type="button" role="tab" aria-selected="false" aria-controls="view-commands" data-view="commands"><span class="nav-icon">›_</span><span class="nav-label">Commands</span><span class="nav-count">${AUVRYNT_COMMANDS.length}</span></button>
|
|
69
|
+
</nav>
|
|
70
|
+
|
|
71
|
+
<div class="rail-spacer"></div>
|
|
72
|
+
<div class="lifecycle">
|
|
73
|
+
<button class="button primary" id="restart" type="button">Restart</button>
|
|
74
|
+
<button class="button stop" id="stop" type="button">Stop</button>
|
|
75
|
+
</div>
|
|
76
|
+
<p class="rail-meta">Local control surface<br />Uptime <span id="rail-uptime">${escapeHtml(formatUptime(view.uptimeSeconds))}</span></p>
|
|
77
|
+
</aside>
|
|
78
|
+
|
|
79
|
+
<main class="workspace">
|
|
80
|
+
<header class="workspace-header">
|
|
81
|
+
<div>
|
|
82
|
+
<p class="page-kicker" id="page-kicker">${PAGE_META.analytics.kicker}</p>
|
|
83
|
+
<h1 class="page-title" id="page-title">${PAGE_META.analytics.title}</h1>
|
|
84
|
+
<p class="page-description" id="page-description" hidden>${PAGE_META.analytics.description}</p>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="server-state">
|
|
87
|
+
<span class="server-state-dot" id="server-state-dot" aria-hidden="true"></span>
|
|
88
|
+
<span class="server-state-copy"><strong id="server-state-title">Server online</strong><small id="server-state-detail">Refreshes every 2 seconds</small></span>
|
|
89
|
+
</div>
|
|
90
|
+
</header>
|
|
91
|
+
|
|
92
|
+
<section class="tunnel-alert" id="tunnel-alert" role="alert" hidden>
|
|
93
|
+
<span class="tunnel-alert-icon" aria-hidden="true">!</span>
|
|
94
|
+
<div class="tunnel-alert-copy"><strong id="tunnel-alert-title"></strong><p id="tunnel-alert-message"></p></div>
|
|
95
|
+
<button class="button" id="open-secrets" type="button">Open Secrets</button>
|
|
96
|
+
</section>
|
|
97
|
+
|
|
98
|
+
<section class="dashboard-view" id="view-analytics" role="tabpanel" aria-labelledby="tab-analytics" data-view-panel="analytics">
|
|
99
|
+
<div class="analytics-lead">
|
|
100
|
+
<section class="activity-surface" id="activity-surface" data-agent-state="${view.agentState}">
|
|
101
|
+
<div class="activity-copy">
|
|
102
|
+
<div>
|
|
103
|
+
<p class="surface-label">Web agent presence</p>
|
|
104
|
+
<h2 class="agent-headline" id="agent-headline">${escapeHtml(agentHeadline(view))}</h2>
|
|
105
|
+
<p class="agent-caption" id="agent-caption">${escapeHtml(agentCaption(view))}</p>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="activity-foot"><span id="agent-sessions">${view.sessions} active session${view.sessions === 1 ? "" : "s"}</span><span id="agent-tools">${view.activeToolCalls} tool call${view.activeToolCalls === 1 ? "" : "s"} running</span><span id="agent-last-seen">${escapeHtml(lastSeenLabel(view.agentLastSeenAt))}</span></div>
|
|
108
|
+
</div>
|
|
109
|
+
</section>
|
|
110
|
+
|
|
111
|
+
<section class="change-surface" aria-labelledby="change-title">
|
|
112
|
+
<div class="change-head"><div><p class="surface-label">Current workspace</p><h2 id="change-title">Agent changes</h2><p class="change-workspace" id="change-workspace" title="${escapeHtml(view.workspaceChanges.workspaceRoot ?? "No workspace opened")}">${escapeHtml(view.workspaceChanges.workspaceRoot ?? "No workspace opened")}</p></div><span class="change-sampled" id="change-sampled">${escapeHtml(agentChangeLabel(view.workspaceChanges.workspaceRoot, view.workspaceChanges.sampledAt))}</span></div>
|
|
113
|
+
<div class="code-delta"><div class="delta add"><strong id="change-additions" title="+${view.workspaceChanges.additions.toLocaleString()}">+${escapeHtml(formatMetricNumber(view.workspaceChanges.additions))}</strong><span>Lines added</span></div><div class="delta remove"><strong id="change-removals" title="−${view.workspaceChanges.removals.toLocaleString()}">−${escapeHtml(formatMetricNumber(view.workspaceChanges.removals))}</strong><span>Lines removed</span></div></div>
|
|
114
|
+
<div class="file-delta"><div><strong id="files-created">${view.workspaceChanges.filesCreated}</strong><span>Created</span></div><div><strong id="files-modified">${view.workspaceChanges.filesModified}</strong><span>Modified</span></div><div><strong id="files-deleted">${view.workspaceChanges.filesDeleted}</strong><span>Deleted</span></div></div>
|
|
115
|
+
</section>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div class="runtime-strip" aria-label="Runtime metrics">
|
|
119
|
+
<div class="runtime-metric"><span>MCP sessions</span><strong id="metric-sessions">${view.sessions} / ${view.maxSessions}</strong></div>
|
|
120
|
+
<div class="runtime-metric"><span>Managed processes</span><strong id="metric-processes">${view.runningProcesses}</strong></div>
|
|
121
|
+
<div class="runtime-metric"><span>Tool calls sampled</span><strong id="metric-tools">0</strong></div>
|
|
122
|
+
<div class="runtime-metric"><span>Average HTTP</span><strong id="metric-http">—</strong></div>
|
|
123
|
+
<div class="runtime-metric"><span>Server uptime</span><strong id="metric-uptime">${escapeHtml(formatUptime(view.uptimeSeconds))}</strong></div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div class="analytics-lower">
|
|
127
|
+
<section class="analytics-panel" aria-labelledby="activity-title">
|
|
128
|
+
<div class="analytics-panel-head"><h3 id="activity-title">Operational activity</h3><small id="analytics-window">Latest 0 events</small></div>
|
|
129
|
+
<div class="activity-chart" id="activity-chart" aria-label="Recent event activity chart"></div>
|
|
130
|
+
<div class="chart-axis"><span>30m ago</span><span>15m</span><span>Now</span></div>
|
|
131
|
+
</section>
|
|
132
|
+
<div class="analytics-stack">
|
|
133
|
+
<section class="analytics-panel" aria-labelledby="tools-title"><div class="analytics-panel-head"><h3 id="tools-title">Most used tools</h3><small>Current sample</small></div><div class="top-tools" id="top-tools"></div></section>
|
|
134
|
+
<section class="analytics-panel" aria-labelledby="health-title"><div class="analytics-panel-head"><h3 id="health-title">Health signals</h3><small>Warnings and failures</small></div><div class="mix-list" id="health-signals"></div></section>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</section>
|
|
138
|
+
|
|
139
|
+
<section class="dashboard-view" id="view-connectivity" role="tabpanel" aria-labelledby="tab-connectivity" data-view-panel="connectivity" hidden>
|
|
140
|
+
<div class="operations-grid">
|
|
141
|
+
<section class="section-surface" aria-labelledby="connection-title">
|
|
142
|
+
<div class="section-head"><div><h2 id="connection-title">Connection</h2><p>Endpoints and the root exposed to web agents.</p></div></div>
|
|
143
|
+
<div class="connection-list">
|
|
144
|
+
<div class="connection-row"><span class="connection-label">Local MCP</span><span class="connection-value"><a id="local-mcp" href="${escapeHtml(view.localMcpUrl)}">${escapeHtml(view.localMcpUrl)}</a></span><button class="icon-button" type="button" data-copy-url="local" aria-label="Copy Local MCP URL" title="Copy Local MCP URL">⧉</button></div>
|
|
145
|
+
<div class="connection-row"><span class="connection-label">Public MCP</span><span class="connection-value"><a id="public-mcp" href="${escapeHtml(view.publicMcpUrl)}">${escapeHtml(view.publicMcpUrl)}</a></span><button class="icon-button" type="button" data-copy-url="public" aria-label="Copy Public MCP URL" title="Copy Public MCP URL">⧉</button></div>
|
|
146
|
+
<div class="connection-row"><span class="connection-label">Workspace</span><span class="connection-value" id="workspace" title="${escapeHtml(firstRoot)}">${escapeHtml(firstRoot || "No workspace configured")}</span><button class="icon-button" id="edit-workspace" type="button" aria-label="Choose workspace folder" title="Choose workspace folder"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 6.5h6l2 2h9v9.75a1.75 1.75 0 0 1-1.75 1.75H5.25a1.75 1.75 0 0 1-1.75-1.75z"/><path d="M3.5 9h17"/></svg></button></div>
|
|
147
|
+
<form class="session-limit-form" id="session-limit-form"><div class="session-limit-copy"><strong>MCP session limit</strong><small>Maximum concurrent connections</small></div><input class="session-limit-input" id="session-limit-input" type="text" inputmode="numeric" pattern="[1-9][0-9]{0,2}" maxlength="3" value="${view.maxSessions}" aria-label="Maximum concurrent MCP sessions" /><button class="icon-button" id="save-session-limit" type="submit" aria-label="Apply MCP session limit" title="Apply MCP session limit"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h4l10.5-10.5a2.12 2.12 0 0 0-3-3L5 17v3z"/><path d="m13.5 8.5 3 3"/></svg></button></form>
|
|
148
|
+
<form class="session-limit-form session-idle-timeout-form" id="session-idle-timeout-form"><div class="session-limit-copy"><strong>MCP idle timeout</strong><small>Inactive sessions close; active tool calls are protected</small></div><input class="session-limit-input" id="session-idle-timeout-input" type="text" inputmode="numeric" pattern="[1-9][0-9]{0,4}" maxlength="5" value="${view.sessionIdleTimeoutMinutes}" aria-label="MCP idle timeout in minutes" /><span class="session-time-unit">min</span><button class="icon-button" id="save-session-idle-timeout" type="submit" aria-label="Apply MCP idle timeout" title="Apply MCP idle timeout"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h4l10.5-10.5a2.12 2.12 0 0 0-3-3L5 17v3z"/><path d="m13.5 8.5 3 3"/></svg></button></form>
|
|
149
|
+
</div>
|
|
150
|
+
</section>
|
|
151
|
+
|
|
152
|
+
<section class="section-surface" aria-labelledby="integrations-title">
|
|
153
|
+
<div class="section-head">
|
|
154
|
+
<div><h2 id="integrations-title">Integrations</h2><p>Availability and live bridge state.</p></div>
|
|
155
|
+
<button class="icon-button" id="open-tool-settings" type="button" aria-label="Open tool settings" title="Tool settings"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/><path d="M19.4 13.5a7.6 7.6 0 0 0 .1-1.5 7.6 7.6 0 0 0-.1-1.5l2-1.5-2-3.5-2.4 1a8.2 8.2 0 0 0-2.6-1.5L14 2.8h-4l-.4 2.7a8.2 8.2 0 0 0-2.6 1.5l-2.4-1-2 3.5 2 1.5a7.6 7.6 0 0 0 0 3l-2 1.5 2 3.5 2.4-1a8.2 8.2 0 0 0 2.6 1.5l.4 2.7h4l.4-2.7a8.2 8.2 0 0 0 2.6-1.5l2.4 1 2-3.5z"/></svg></button>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="integration-list" id="integrations"></div>
|
|
158
|
+
</section>
|
|
159
|
+
|
|
160
|
+
<section class="section-surface skills-span" aria-labelledby="connectivity-skills-title">
|
|
161
|
+
<div class="section-head">
|
|
162
|
+
<div><h2 id="connectivity-skills-title">Skills</h2><p>Installed Agent Skills available to agents on open_workspace.</p></div>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="skills-meta" id="skills-meta-connectivity"></div>
|
|
165
|
+
<div class="skill-list" id="skill-list-connectivity"></div>
|
|
166
|
+
</section>
|
|
167
|
+
</div>
|
|
168
|
+
</section>
|
|
169
|
+
<section class="dashboard-view" id="view-secrets" role="tabpanel" aria-labelledby="tab-secrets" data-view-panel="secrets" hidden>
|
|
170
|
+
<div class="secrets-layout">
|
|
171
|
+
<section class="section-surface" aria-labelledby="secret-add-title">
|
|
172
|
+
<div class="section-head"><div><h2 id="secret-add-title">Add ngrok authtoken</h2><p>Values stay in the private local Auvrynt auth file. Copy the current secret only when you explicitly request it.</p></div><button class="button section-action" id="copy-ngrok-secret" type="button" title="Copy current ngrok secret" aria-label="Copy current ngrok secret">Copy secret</button></div>
|
|
173
|
+
<form class="secret-form" id="ngrok-token-form" autocomplete="off">
|
|
174
|
+
<label class="secret-label" for="ngrok-token-input">ngrok authtoken</label>
|
|
175
|
+
<div class="secret-input-row"><input class="secret-input" id="ngrok-token-input" type="password" minlength="16" maxlength="512" spellcheck="false" autocomplete="new-password" placeholder="Paste a backup authtoken" /><button class="button primary" id="add-ngrok-token" type="submit">Add token</button></div>
|
|
176
|
+
<p class="secret-help">Auvrynt identifies saved tokens only by a one-way SHA-256 fingerprint. On ERR_NGROK_727 it marks the active token exhausted, selects the next available token, and recreates the tunnel. Backup tokens must belong to different ngrok accounts because tokens on one account share the same quota. Unless you configured a stable ngrok URL, the public MCP URL may change and must be updated in the connector.</p>
|
|
177
|
+
</form>
|
|
178
|
+
<p class="secret-warning" id="ngrok-environment-warning" hidden>AUVRYNT_NGROK_AUTHTOKEN currently overrides this pool. Remove the environment variable before automatic switching can use saved tokens.</p>
|
|
179
|
+
</section>
|
|
180
|
+
|
|
181
|
+
<section class="section-surface" aria-labelledby="secret-list-title">
|
|
182
|
+
<div class="section-head"><div><h2 id="secret-list-title">Saved ngrok tokens</h2><p>Only fingerprints and health state are displayed.</p></div></div>
|
|
183
|
+
<div class="token-list" id="ngrok-token-list"></div>
|
|
184
|
+
</section>
|
|
185
|
+
|
|
186
|
+
<section class="section-surface" aria-labelledby="context7-secret-title">
|
|
187
|
+
<div class="section-head"><div><h2 id="context7-secret-title">Context7 API key</h2><p>Stored only in the private local Auvrynt auth file. The raw key is never shown again.</p></div></div>
|
|
188
|
+
<form class="secret-form" id="context7-key-form" autocomplete="off">
|
|
189
|
+
<label class="secret-label" for="context7-key-input">Context7 API key</label>
|
|
190
|
+
<div class="secret-input-row"><input class="secret-input" id="context7-key-input" type="password" minlength="8" maxlength="512" spellcheck="false" autocomplete="new-password" placeholder="Paste Context7 API key" /><button class="button primary" id="save-context7-key" type="submit">Save key</button></div>
|
|
191
|
+
<p class="secret-help">Get a free key at context7.com/dashboard. Auvrynt keeps only a SHA-256 fingerprint on this page. Override with AUVRYNT_CONTEXT7_API_KEY if needed.</p>
|
|
192
|
+
</form>
|
|
193
|
+
<div class="token-list" id="context7-key-status"></div>
|
|
194
|
+
<p class="secret-warning" id="context7-environment-warning" hidden>AUVRYNT_CONTEXT7_API_KEY currently overrides the saved key. Remove the environment variable to manage it here.</p>
|
|
195
|
+
</section>
|
|
196
|
+
</div>
|
|
197
|
+
</section>
|
|
198
|
+
|
|
199
|
+
<section class="dashboard-view" id="view-logs" role="tabpanel" aria-labelledby="tab-logs" data-view-panel="logs" hidden>
|
|
200
|
+
<div class="view-toolbar logs-toolbar"><h2>Recent events</h2><input class="search" id="log-search" type="search" placeholder="Search logs…" autocomplete="off" /><div class="log-filter-row" role="group" aria-label="Log categories"><button class="filter active" data-log-filter="all" type="button">All</button><button class="filter" data-log-filter="tool" type="button">Tool</button><button class="filter" data-log-filter="mcp" type="button">MCP</button><button class="filter" data-log-filter="http" type="button">HTTP</button><button class="filter" data-log-filter="auth" type="button">Auth</button><button class="filter" data-log-filter="error" type="button">Error</button></div></div>
|
|
201
|
+
<div class="logs-wrap"><button class="button new-logs" id="new-logs" type="button"></button><div class="logs" id="log-list" tabindex="0" aria-label="Recent Auvrynt logs"></div></div>
|
|
202
|
+
</section>
|
|
203
|
+
|
|
204
|
+
<section class="dashboard-view" id="view-commands" role="tabpanel" aria-labelledby="tab-commands" data-view-panel="commands" hidden>
|
|
205
|
+
<div class="view-toolbar command-toolbar"><div><h2>Command reference</h2><p class="page-description">Browse Auvrynt commands and copy them for use in a local terminal.</p></div><input class="search" id="command-search" type="search" placeholder="Filter commands…" autocomplete="off" /></div>
|
|
206
|
+
<div class="commands" id="command-list">${commands}</div>
|
|
207
|
+
</section>
|
|
208
|
+
|
|
209
|
+
<section class="dashboard-view" id="view-tools" role="tabpanel" aria-labelledby="tab-tools" data-view-panel="tools" hidden>
|
|
210
|
+
<div class="view-toolbar tools-toolbar">
|
|
211
|
+
<div class="tools-tab-stack">
|
|
212
|
+
<div class="tools-integration-tabs" id="tools-integration-tabs" role="tablist" aria-label="Integration tool settings"></div>
|
|
213
|
+
<div class="tools-sub-tabs" id="tools-sub-tabs" role="tablist" aria-label="Integration tool subgroups" hidden></div>
|
|
214
|
+
</div>
|
|
215
|
+
<input class="search" id="tools-search" type="search" placeholder="Filter tools…" autocomplete="off" />
|
|
216
|
+
</div>
|
|
217
|
+
<div class="tools-layout" id="tools-layout"></div>
|
|
218
|
+
</section>
|
|
219
|
+
</main>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<div class="lifecycle-overlay" id="lifecycle-overlay" role="status" aria-live="assertive" aria-hidden="true"><div class="lifecycle-panel"><div class="lifecycle-spinner" aria-hidden="true"></div><h2 class="lifecycle-title" id="lifecycle-title">Restarting Auvrynt</h2><p class="lifecycle-message" id="lifecycle-message">Please wait while local services reconnect.</p><div class="lifecycle-progress" aria-hidden="true"></div></div></div>
|
|
223
|
+
<dialog class="confirm-dialog" id="confirm-dialog" aria-labelledby="confirm-title" aria-describedby="confirm-message"><div class="confirm-body"><p class="confirm-kicker">Auvrynt control</p><h2 class="confirm-title" id="confirm-title">Confirm action</h2><p class="confirm-message" id="confirm-message"></p></div><div class="confirm-actions"><button class="button" id="confirm-cancel" type="button">Cancel</button><button class="button primary" id="confirm-accept" type="button">Continue</button></div></dialog>
|
|
224
|
+
<div class="toast-region" aria-live="polite" aria-atomic="true"><div class="toast" id="action-notice" role="status"></div></div>
|
|
225
|
+
|
|
226
|
+
<script nonce="${escapeHtml(nonce)}">
|
|
227
|
+
(() => {
|
|
228
|
+
const pageMeta = ${jsonForHtml(PAGE_META)};
|
|
229
|
+
let state = ${initialData};
|
|
230
|
+
let activeFilter = "all";
|
|
231
|
+
let knownNewestId = state.logs[0]?.id ?? 0;
|
|
232
|
+
let unseenLogs = 0;
|
|
233
|
+
let lifecycleMode = null;
|
|
234
|
+
let refreshFailures = 0;
|
|
235
|
+
const viewNames = ["analytics", "connectivity", "secrets", "logs", "commands", "tools"];
|
|
236
|
+
let activeView = viewNames.includes(window.location.hash.slice(1)) ? window.location.hash.slice(1) : "analytics";
|
|
237
|
+
const logs = document.getElementById("log-list");
|
|
238
|
+
const integrations = document.getElementById("integrations");
|
|
239
|
+
const skillList = document.getElementById("skill-list-connectivity");
|
|
240
|
+
const skillsMeta = document.getElementById("skills-meta-connectivity");
|
|
241
|
+
const notice = document.getElementById("action-notice");
|
|
242
|
+
const newLogs = document.getElementById("new-logs");
|
|
243
|
+
const logSearch = document.getElementById("log-search");
|
|
244
|
+
const activityChart = document.getElementById("activity-chart");
|
|
245
|
+
const topTools = document.getElementById("top-tools");
|
|
246
|
+
const healthSignals = document.getElementById("health-signals");
|
|
247
|
+
const tunnelAlert = document.getElementById("tunnel-alert");
|
|
248
|
+
const tunnelAlertTitle = document.getElementById("tunnel-alert-title");
|
|
249
|
+
const tunnelAlertMessage = document.getElementById("tunnel-alert-message");
|
|
250
|
+
const openSecretsButton = document.getElementById("open-secrets");
|
|
251
|
+
const ngrokTokenForm = document.getElementById("ngrok-token-form");
|
|
252
|
+
const ngrokTokenInput = document.getElementById("ngrok-token-input");
|
|
253
|
+
const addNgrokTokenButton = document.getElementById("add-ngrok-token");
|
|
254
|
+
const copyNgrokSecretButton = document.getElementById("copy-ngrok-secret");
|
|
255
|
+
const ngrokTokenList = document.getElementById("ngrok-token-list");
|
|
256
|
+
const ngrokEnvironmentWarning = document.getElementById("ngrok-environment-warning");
|
|
257
|
+
const context7KeyForm = document.getElementById("context7-key-form");
|
|
258
|
+
const context7KeyInput = document.getElementById("context7-key-input");
|
|
259
|
+
const saveContext7KeyButton = document.getElementById("save-context7-key");
|
|
260
|
+
const context7KeyStatus = document.getElementById("context7-key-status");
|
|
261
|
+
const context7EnvironmentWarning = document.getElementById("context7-environment-warning");
|
|
262
|
+
const restartButton = document.getElementById("restart");
|
|
263
|
+
const stopButton = document.getElementById("stop");
|
|
264
|
+
const lifecycleOverlay = document.getElementById("lifecycle-overlay");
|
|
265
|
+
const lifecycleTitle = document.getElementById("lifecycle-title");
|
|
266
|
+
const lifecycleMessage = document.getElementById("lifecycle-message");
|
|
267
|
+
const confirmDialog = document.getElementById("confirm-dialog");
|
|
268
|
+
const confirmTitle = document.getElementById("confirm-title");
|
|
269
|
+
const confirmMessage = document.getElementById("confirm-message");
|
|
270
|
+
const confirmCancel = document.getElementById("confirm-cancel");
|
|
271
|
+
const confirmAccept = document.getElementById("confirm-accept");
|
|
272
|
+
const sessionLimitForm = document.getElementById("session-limit-form");
|
|
273
|
+
const sessionLimitInput = document.getElementById("session-limit-input");
|
|
274
|
+
const saveSessionLimitButton = document.getElementById("save-session-limit");
|
|
275
|
+
const sessionIdleTimeoutForm = document.getElementById("session-idle-timeout-form");
|
|
276
|
+
const sessionIdleTimeoutInput = document.getElementById("session-idle-timeout-input");
|
|
277
|
+
const saveSessionIdleTimeoutButton = document.getElementById("save-session-idle-timeout");
|
|
278
|
+
const toolsIntegrationTabs = document.getElementById("tools-integration-tabs");
|
|
279
|
+
const toolsSubTabs = document.getElementById("tools-sub-tabs");
|
|
280
|
+
const toolsSearch = document.getElementById("tools-search");
|
|
281
|
+
const toolsLayout = document.getElementById("tools-layout");
|
|
282
|
+
let toolsPrimary = null;
|
|
283
|
+
let toolsSub = null;
|
|
284
|
+
let toolsIntegration = null;
|
|
285
|
+
let toolsData = null;
|
|
286
|
+
let toolsFilter = "";
|
|
287
|
+
let toolsBusy = new Set();
|
|
288
|
+
const toolsOpenGroups = new Set();
|
|
289
|
+
let toolsRendering = false;
|
|
290
|
+
const openToolSettingsButton = document.getElementById("open-tool-settings");
|
|
291
|
+
let confirmResolver = null;
|
|
292
|
+
let refreshTimer = null;
|
|
293
|
+
|
|
294
|
+
const category = (entry) => {
|
|
295
|
+
const event = entry.event.toLowerCase();
|
|
296
|
+
if (entry.level === "error") return "error";
|
|
297
|
+
if (event.includes("tool")) return "tool";
|
|
298
|
+
if (event.includes("mcp") || event.includes("session")) return "mcp";
|
|
299
|
+
if (event.includes("auth") || event.includes("oauth") || event.includes("token")) return "auth";
|
|
300
|
+
if (event.includes("http")) return "http";
|
|
301
|
+
return "other";
|
|
302
|
+
};
|
|
303
|
+
const escape = (value) => String(value).replace(/[&<>"']/g, (char) => ({"&":"&","<":"<",">":">",'"':""","'":"'"})[char]);
|
|
304
|
+
const time = (value) => new Date(value).toLocaleTimeString([], { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
305
|
+
const uptime = (seconds) => {
|
|
306
|
+
const total = Math.max(0, Math.floor(seconds));
|
|
307
|
+
const days = Math.floor(total / 86400);
|
|
308
|
+
const hours = Math.floor((total % 86400) / 3600);
|
|
309
|
+
const minutes = Math.floor((total % 3600) / 60);
|
|
310
|
+
return [days ? days + "d" : "", hours ? hours + "h" : "", minutes + "m"].filter(Boolean).join(" ");
|
|
311
|
+
};
|
|
312
|
+
const relativeTime = (value) => {
|
|
313
|
+
if (!value) return "No agent activity yet";
|
|
314
|
+
const seconds = Math.max(0, Math.round((Date.now() - Date.parse(value)) / 1000));
|
|
315
|
+
if (seconds < 5) return "Active just now";
|
|
316
|
+
if (seconds < 60) return "Active " + seconds + "s ago";
|
|
317
|
+
if (seconds < 3600) return "Active " + Math.floor(seconds / 60) + "m ago";
|
|
318
|
+
return "Active " + Math.floor(seconds / 3600) + "h ago";
|
|
319
|
+
};
|
|
320
|
+
const levelClass = (value, maximum) => "level-" + Math.max(0, Math.min(10, maximum > 0 ? Math.round((value / maximum) * 10) : 0));
|
|
321
|
+
const delay = (ms) => new Promise((resolve) => window.setTimeout(resolve, ms));
|
|
322
|
+
const exactNumber = (value) => new Intl.NumberFormat().format(Math.max(0, Number(value) || 0));
|
|
323
|
+
const compactNumber = (value) => {
|
|
324
|
+
const normalized = Math.max(0, Number(value) || 0);
|
|
325
|
+
return normalized < 100000
|
|
326
|
+
? exactNumber(normalized)
|
|
327
|
+
: new Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1 }).format(normalized);
|
|
328
|
+
};
|
|
329
|
+
let toastTimer = null;
|
|
330
|
+
|
|
331
|
+
function showToast(message, tone = "info", duration = 3200) {
|
|
332
|
+
if (toastTimer) window.clearTimeout(toastTimer);
|
|
333
|
+
toastTimer = null;
|
|
334
|
+
notice.textContent = message;
|
|
335
|
+
notice.classList.toggle("error", tone === "error");
|
|
336
|
+
notice.classList.remove("visible");
|
|
337
|
+
void notice.offsetWidth;
|
|
338
|
+
notice.classList.add("visible");
|
|
339
|
+
if (duration > 0) {
|
|
340
|
+
toastTimer = window.setTimeout(() => {
|
|
341
|
+
notice.classList.remove("visible");
|
|
342
|
+
toastTimer = null;
|
|
343
|
+
window.setTimeout(() => {
|
|
344
|
+
if (!notice.classList.contains("visible")) notice.textContent = "";
|
|
345
|
+
}, 240);
|
|
346
|
+
}, duration);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function finishConfirmation(accepted) {
|
|
351
|
+
const resolve = confirmResolver;
|
|
352
|
+
confirmResolver = null;
|
|
353
|
+
if (confirmDialog.open) confirmDialog.close();
|
|
354
|
+
resolve?.(accepted);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function confirmAction(title, message, acceptLabel = "Continue") {
|
|
358
|
+
if (confirmResolver) finishConfirmation(false);
|
|
359
|
+
confirmTitle.textContent = title;
|
|
360
|
+
confirmMessage.textContent = message;
|
|
361
|
+
confirmAccept.textContent = acceptLabel;
|
|
362
|
+
confirmDialog.showModal();
|
|
363
|
+
confirmCancel.focus();
|
|
364
|
+
return new Promise((resolve) => { confirmResolver = resolve; });
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function setChangeMetric(id, value, prefix) {
|
|
368
|
+
const element = document.getElementById(id);
|
|
369
|
+
const exact = prefix + exactNumber(value);
|
|
370
|
+
element.textContent = prefix + compactNumber(value);
|
|
371
|
+
element.title = exact;
|
|
372
|
+
element.setAttribute("aria-label", exact);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function agentText(nextState) {
|
|
376
|
+
const provider = nextState.agentProvider || "AI agent";
|
|
377
|
+
if (nextState.agentState === "working") return { title: provider + " working", headline: provider + " is actively using Auvrynt.", caption: nextState.activeToolCalls + " tool call" + (nextState.activeToolCalls === 1 ? " is" : "s are") + " executing now." };
|
|
378
|
+
if (nextState.agentState === "connected") return { title: provider + " connected", headline: provider + " is connected and ready.", caption: nextState.sessions + " authenticated MCP session" + (nextState.sessions === 1 ? " is" : "s are") + " currently open." };
|
|
379
|
+
if (nextState.agentState === "stopping") return { title: "Server stopping", headline: "Auvrynt is closing active connections.", caption: "The dashboard will stop refreshing when shutdown completes." };
|
|
380
|
+
return { title: "Waiting for AI", headline: "Auvrynt is online and ready.", caption: "Connect ChatGPT, Claude, or another MCP-compatible AI client." };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function setActiveView(view, updateHash = true) {
|
|
384
|
+
activeView = viewNames.includes(view) ? view : "analytics";
|
|
385
|
+
document.querySelectorAll("[data-view]").forEach((tab) => {
|
|
386
|
+
const selected = tab.dataset.view === activeView;
|
|
387
|
+
tab.setAttribute("aria-selected", String(selected));
|
|
388
|
+
tab.tabIndex = selected ? 0 : -1;
|
|
389
|
+
});
|
|
390
|
+
document.querySelectorAll("[data-view-panel]").forEach((panel) => { panel.hidden = panel.dataset.viewPanel !== activeView; });
|
|
391
|
+
document.getElementById("page-kicker").textContent = pageMeta[activeView].kicker;
|
|
392
|
+
document.getElementById("page-title").textContent = pageMeta[activeView].title;
|
|
393
|
+
const pageDescription = document.getElementById("page-description");
|
|
394
|
+
pageDescription.textContent = pageMeta[activeView].description;
|
|
395
|
+
pageDescription.hidden = !pageMeta[activeView].description;
|
|
396
|
+
if (updateHash) window.history.replaceState(null, "", "#" + activeView);
|
|
397
|
+
if (activeView === "tools") {
|
|
398
|
+
// Populate tabs on hash load / keyboard nav, not only gear click.
|
|
399
|
+
if (!toolsPrimary) resolveToolsSelection(null);
|
|
400
|
+
renderToolsTabs();
|
|
401
|
+
if (!toolsData) void loadActiveToolsPanel();
|
|
402
|
+
else renderTools();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function renderAgentPresence() {
|
|
407
|
+
const copy = agentText(state);
|
|
408
|
+
const presence = document.getElementById("agent-presence");
|
|
409
|
+
presence.dataset.state = state.agentState;
|
|
410
|
+
document.getElementById("presence-title").textContent = copy.title;
|
|
411
|
+
document.getElementById("presence-detail").textContent = state.agentState === "waiting" ? copy.caption : relativeTime(state.agentLastSeenAt);
|
|
412
|
+
const activitySurface = document.getElementById("activity-surface");
|
|
413
|
+
activitySurface.dataset.agentState = state.agentState;
|
|
414
|
+
document.getElementById("agent-headline").textContent = copy.headline;
|
|
415
|
+
document.getElementById("agent-caption").textContent = copy.caption;
|
|
416
|
+
document.getElementById("agent-sessions").textContent = state.sessions + " active session" + (state.sessions === 1 ? "" : "s");
|
|
417
|
+
document.getElementById("agent-tools").textContent = state.activeToolCalls + " tool call" + (state.activeToolCalls === 1 ? "" : "s") + " running";
|
|
418
|
+
document.getElementById("agent-last-seen").textContent = relativeTime(state.agentLastSeenAt);
|
|
419
|
+
document.getElementById("nav-session-count").textContent = state.sessions + "/" + state.maxSessions;
|
|
420
|
+
if (document.activeElement !== sessionLimitInput && !sessionLimitInput.disabled) {
|
|
421
|
+
sessionLimitInput.value = String(state.maxSessions);
|
|
422
|
+
}
|
|
423
|
+
if (document.activeElement !== sessionIdleTimeoutInput && !sessionIdleTimeoutInput.disabled) {
|
|
424
|
+
sessionIdleTimeoutInput.value = String(state.sessionIdleTimeoutMinutes);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function operationalEntries() {
|
|
429
|
+
return state.logs.filter((entry) => {
|
|
430
|
+
if (entry.event !== "http_request") return true;
|
|
431
|
+
const path = typeof entry.fields.path === "string" ? entry.fields.path : "";
|
|
432
|
+
return !path.startsWith("/dashboard") && !path.startsWith("/brand-assets/");
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function renderAnalytics() {
|
|
437
|
+
const entries = operationalEntries();
|
|
438
|
+
const toolCalls = entries.filter((entry) => entry.event === "tool_call");
|
|
439
|
+
const errors = entries.filter((entry) => entry.level === "error");
|
|
440
|
+
const httpDurations = entries.filter((entry) => entry.event === "http_request" && Number.isFinite(Number(entry.fields.durationMs))).map((entry) => Number(entry.fields.durationMs));
|
|
441
|
+
const averageHttp = httpDurations.length ? httpDurations.reduce((total, value) => total + value, 0) / httpDurations.length : null;
|
|
442
|
+
|
|
443
|
+
document.getElementById("metric-sessions").textContent = state.sessions + " / " + state.maxSessions;
|
|
444
|
+
document.getElementById("metric-processes").textContent = String(state.runningProcesses);
|
|
445
|
+
document.getElementById("metric-tools").textContent = String(toolCalls.length);
|
|
446
|
+
document.getElementById("metric-http").textContent = averageHttp === null ? "—" : averageHttp < 1000 ? Math.round(averageHttp) + "ms" : (averageHttp / 1000).toFixed(1) + "s";
|
|
447
|
+
document.getElementById("metric-uptime").textContent = uptime(state.uptimeSeconds);
|
|
448
|
+
document.getElementById("rail-uptime").textContent = uptime(state.uptimeSeconds);
|
|
449
|
+
document.getElementById("rail-pid").textContent = String(state.pid);
|
|
450
|
+
document.getElementById("analytics-window").textContent = "Latest " + entries.length + " operational events";
|
|
451
|
+
document.getElementById("nav-log-count").textContent = String(state.logs.length);
|
|
452
|
+
|
|
453
|
+
const changes = state.workspaceChanges;
|
|
454
|
+
setChangeMetric("change-additions", changes.additions, "+");
|
|
455
|
+
setChangeMetric("change-removals", changes.removals, "−");
|
|
456
|
+
document.getElementById("files-created").textContent = exactNumber(changes.filesCreated);
|
|
457
|
+
document.getElementById("files-modified").textContent = exactNumber(changes.filesModified);
|
|
458
|
+
document.getElementById("files-deleted").textContent = exactNumber(changes.filesDeleted);
|
|
459
|
+
const changedWorkspace = changes.workspaceRoot || "No workspace opened";
|
|
460
|
+
const changedWorkspaceElement = document.getElementById("change-workspace");
|
|
461
|
+
changedWorkspaceElement.textContent = changedWorkspace;
|
|
462
|
+
changedWorkspaceElement.title = changedWorkspace;
|
|
463
|
+
document.getElementById("change-sampled").textContent = changes.workspaceRoot
|
|
464
|
+
? "Auvrynt · " + relativeTime(changes.sampledAt).replace("Active", "Updated")
|
|
465
|
+
: "No agent changes yet";
|
|
466
|
+
|
|
467
|
+
const now = Date.now();
|
|
468
|
+
const windowMs = 30 * 60 * 1000;
|
|
469
|
+
const bucketCount = 12;
|
|
470
|
+
const bucketMs = windowMs / bucketCount;
|
|
471
|
+
const bucketStart = now - windowMs;
|
|
472
|
+
const buckets = Array.from({ length: bucketCount }, () => 0);
|
|
473
|
+
entries.forEach((entry) => {
|
|
474
|
+
const timestamp = Date.parse(entry.ts);
|
|
475
|
+
if (!Number.isFinite(timestamp) || timestamp < bucketStart || timestamp > now) return;
|
|
476
|
+
const index = Math.min(bucketCount - 1, Math.floor((timestamp - bucketStart) / bucketMs));
|
|
477
|
+
buckets[index] += 1;
|
|
478
|
+
});
|
|
479
|
+
const peak = Math.max(1, ...buckets);
|
|
480
|
+
activityChart.innerHTML = buckets.map((count, index) => '<span class="activity-bar ' + levelClass(count, peak) + '" title="Bucket ' + (index + 1) + ': ' + count + ' events"></span>').join("");
|
|
481
|
+
|
|
482
|
+
const toolCounts = new Map();
|
|
483
|
+
toolCalls.forEach((entry) => {
|
|
484
|
+
const tool = typeof entry.fields.tool === "string" ? entry.fields.tool : "unknown";
|
|
485
|
+
toolCounts.set(tool, (toolCounts.get(tool) ?? 0) + 1);
|
|
486
|
+
});
|
|
487
|
+
const rankedTools = [...toolCounts.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).slice(0, 6);
|
|
488
|
+
topTools.innerHTML = rankedTools.length ? rankedTools.map(([name, count]) => '<div class="top-tool"><span class="top-tool-name" title="' + escape(name) + '">' + escape(name) + '</span><strong>' + count + '</strong></div>').join("") : '<div class="analytics-empty">No tool calls in the current sample.</div>';
|
|
489
|
+
|
|
490
|
+
const health = [
|
|
491
|
+
["Warnings", entries.filter((entry) => entry.level === "warn").length],
|
|
492
|
+
["Errors", errors.length],
|
|
493
|
+
["Failed tools", toolCalls.filter((entry) => entry.fields.success === false).length],
|
|
494
|
+
["Closed sessions", entries.filter((entry) => entry.event === "mcp_session_closed").length],
|
|
495
|
+
];
|
|
496
|
+
const healthPeak = Math.max(1, ...health.map((item) => item[1]));
|
|
497
|
+
healthSignals.innerHTML = health.map(([name, count]) => '<div class="mix-row"><span class="mix-label">' + escape(name) + '</span><span class="mix-track"><span class="mix-fill ' + levelClass(count, healthPeak) + '"></span></span><span class="mix-count">' + count + '</span></div>').join("");
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function firstVisibleAnchor() {
|
|
501
|
+
const rows = [...logs.querySelectorAll(".log-row")];
|
|
502
|
+
const row = rows.find((item) => item.offsetTop + item.offsetHeight >= logs.scrollTop);
|
|
503
|
+
return row ? { id: row.dataset.logId, offset: row.offsetTop - logs.scrollTop } : null;
|
|
504
|
+
}
|
|
505
|
+
function updateNewLogButton() {
|
|
506
|
+
newLogs.textContent = unseenLogs === 1 ? "↑ 1 new log" : "↑ " + unseenLogs + " new logs";
|
|
507
|
+
newLogs.classList.toggle("visible", unseenLogs > 0 && logs.scrollTop > 4);
|
|
508
|
+
}
|
|
509
|
+
function renderLogs() {
|
|
510
|
+
const atTop = logs.scrollTop <= 4;
|
|
511
|
+
const anchor = atTop ? null : firstVisibleAnchor();
|
|
512
|
+
const query = logSearch.value.trim().toLowerCase();
|
|
513
|
+
const visible = state.logs.filter((entry) => {
|
|
514
|
+
const entryCategory = category(entry);
|
|
515
|
+
if (activeFilter === "error" && entry.level !== "error") return false;
|
|
516
|
+
if (activeFilter !== "all" && activeFilter !== "error" && entryCategory !== activeFilter) return false;
|
|
517
|
+
return !query || (entry.ts + " " + entry.level + " " + entry.event + " " + JSON.stringify(entry.fields)).toLowerCase().includes(query);
|
|
518
|
+
});
|
|
519
|
+
logs.innerHTML = visible.length ? visible.map((entry) => '<div class="log-row" data-log-id="' + entry.id + '"><time>' + time(entry.ts) + '</time><span class="log-level ' + entry.level + '">' + escape(entry.level.toUpperCase()) + '</span><span class="log-event">' + escape(entry.event) + '</span><span class="log-fields">' + escape(Object.keys(entry.fields).length ? JSON.stringify(entry.fields) : "") + '</span></div>').join("") : '<div class="empty">No matching events.</div>';
|
|
520
|
+
if (atTop) { logs.scrollTop = 0; unseenLogs = 0; }
|
|
521
|
+
else if (anchor) {
|
|
522
|
+
const anchored = logs.querySelector('[data-log-id="' + anchor.id + '"]');
|
|
523
|
+
if (anchored) logs.scrollTop = anchored.offsetTop - anchor.offset;
|
|
524
|
+
}
|
|
525
|
+
updateNewLogButton();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function renderIntegrations() {
|
|
529
|
+
integrations.innerHTML = state.integrations.map((item) => {
|
|
530
|
+
const countBadge = item.toolCount > 0
|
|
531
|
+
? '<span class="tool-count" title="' + item.toolEnabledCount + ' of ' + item.toolCount + ' tools enabled"><strong>' + item.toolEnabledCount + '</strong>/' + item.toolCount + '</span>'
|
|
532
|
+
: "";
|
|
533
|
+
return '<div class="integration-row"><div class="integration-name"><strong>' + escape(item.label) + '</strong><small>' + escape(item.detail) + '</small></div>' + countBadge + '<span class="state ' + item.state + '">' + escape(item.state) + '</span><button class="button integration-toggle" type="button" data-integration="' + escape(item.key) + '" data-enabled="' + item.enabled + '">' + (item.enabled ? "Disable" : "Enable") + '</button></div>';
|
|
534
|
+
}).join("");
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function renderSkills() {
|
|
538
|
+
const skillsState = state.skills || { enabled: false, agentDir: "", skillPaths: [], skills: [], diagnostics: [] };
|
|
539
|
+
if (skillsMeta) {
|
|
540
|
+
const rows = [];
|
|
541
|
+
rows.push('<div class="connection-row"><span class="connection-label">Status</span><span class="connection-value">' + (skillsState.enabled ? 'Skills enabled' : 'Skills disabled (AUVRYNT_SKILLS=false)') + '</span></div>');
|
|
542
|
+
rows.push('<div class="connection-row"><span class="connection-label">Agent dir</span><span class="connection-value" title="' + escape(skillsState.agentDir || '') + '">' + escape(skillsState.agentDir || '—') + '</span></div>');
|
|
543
|
+
if (skillsState.workspaceRoot) {
|
|
544
|
+
rows.push('<div class="connection-row"><span class="connection-label">Workspace</span><span class="connection-value" title="' + escape(skillsState.workspaceRoot) + '">' + escape(skillsState.workspaceRoot) + '</span></div>');
|
|
545
|
+
}
|
|
546
|
+
const extra = (skillsState.skillPaths || []).length ? skillsState.skillPaths.join(', ') : 'none';
|
|
547
|
+
rows.push('<div class="connection-row"><span class="connection-label">Extra paths</span><span class="connection-value" title="' + escape(extra) + '">' + escape(extra) + '</span></div>');
|
|
548
|
+
rows.push('<div class="connection-row"><span class="connection-label">Agent access</span><span class="connection-value">Returned on open_workspace as available skills</span></div>');
|
|
549
|
+
skillsMeta.innerHTML = rows.join('');
|
|
550
|
+
}
|
|
551
|
+
if (!skillList) return;
|
|
552
|
+
if (!skillsState.enabled) {
|
|
553
|
+
skillList.innerHTML = '<div class="empty">Skills are disabled. Set AUVRYNT_SKILLS=true (default) to load Agent Skills.</div>';
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const skills = skillsState.skills || [];
|
|
557
|
+
if (!skills.length) {
|
|
558
|
+
skillList.innerHTML = '<div class="empty">No skills discovered under agent dir, project .pi/skills, or AUVRYNT_SKILL_PATHS.</div>';
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
skillList.innerHTML = skills.map((skill) => {
|
|
562
|
+
const badge = skill.disableModelInvocation ? 'manual' : skill.scope;
|
|
563
|
+
const badgeClass = skill.disableModelInvocation ? 'exhausted' : (skill.scope === 'project' ? 'active' : '');
|
|
564
|
+
return '<div class="skill-row"><div class="skill-name"><strong>' + escape(skill.name) + '</strong></div><span class="token-status ' + badgeClass + '">' + escape(badge) + '</span></div>';
|
|
565
|
+
}).join('');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function renderNgrok() {
|
|
569
|
+
const ngrok = state.ngrok;
|
|
570
|
+
document.getElementById("nav-token-count").textContent = String(ngrok.tokens.length);
|
|
571
|
+
copyNgrokSecretButton.disabled = !ngrok.enabled && !ngrok.environmentOverride;
|
|
572
|
+
ngrokEnvironmentWarning.hidden = !ngrok.environmentOverride;
|
|
573
|
+
const noticeData = ngrok.notice;
|
|
574
|
+
tunnelAlert.hidden = !noticeData;
|
|
575
|
+
tunnelAlert.classList.toggle("error", noticeData?.tone === "error");
|
|
576
|
+
if (noticeData) {
|
|
577
|
+
tunnelAlertTitle.textContent = noticeData.title;
|
|
578
|
+
tunnelAlertMessage.textContent = noticeData.message;
|
|
579
|
+
} else {
|
|
580
|
+
tunnelAlertTitle.textContent = "";
|
|
581
|
+
tunnelAlertMessage.textContent = "";
|
|
582
|
+
}
|
|
583
|
+
ngrokTokenList.innerHTML = ngrok.tokens.length
|
|
584
|
+
? ngrok.tokens.map((token) => {
|
|
585
|
+
const exhausted = Boolean(token.quotaExhaustedAt);
|
|
586
|
+
const status = exhausted ? "Quota exhausted" : token.active ? "Active" : "Standby";
|
|
587
|
+
const statusClass = exhausted ? "exhausted" : token.active ? "active" : "";
|
|
588
|
+
const detail = exhausted
|
|
589
|
+
? "Limit detected " + new Date(token.quotaExhaustedAt).toLocaleString()
|
|
590
|
+
: token.active ? "Used for the next ngrok tunnel start" : "Available for automatic failover";
|
|
591
|
+
const activateLabel = exhausted ? "Retry" : "Use";
|
|
592
|
+
const activateButton = token.active && !exhausted
|
|
593
|
+
? ""
|
|
594
|
+
: '<button class="button token-action" type="button" data-ngrok-action="activate" data-ngrok-index="' + token.index + '">' + activateLabel + '</button>';
|
|
595
|
+
return '<div class="token-row"><div class="token-name"><strong>' + escape(token.fingerprint) + '</strong><small>' + escape(detail) + '</small></div><span class="token-status ' + statusClass + '">' + status + '</span><div class="token-actions">' + activateButton + '<button class="button token-action stop" type="button" data-ngrok-action="remove" data-ngrok-index="' + token.index + '">Remove</button></div></div>';
|
|
596
|
+
}).join("")
|
|
597
|
+
: '<div class="empty">No managed ngrok tokens saved.</div>';
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function renderContext7() {
|
|
601
|
+
const context7 = state.context7 || { configured: false, environmentOverride: false };
|
|
602
|
+
context7EnvironmentWarning.hidden = !context7.environmentOverride;
|
|
603
|
+
context7KeyInput.disabled = Boolean(context7.environmentOverride);
|
|
604
|
+
saveContext7KeyButton.disabled = Boolean(context7.environmentOverride);
|
|
605
|
+
if (context7.configured) {
|
|
606
|
+
const source = context7.environmentOverride ? 'Environment override' : 'Saved in local auth file';
|
|
607
|
+
context7KeyStatus.innerHTML = '<div class="token-row"><div class="token-name"><strong>' + escape(context7.fingerprint || 'configured') + '</strong><small>' + escape(source) + '</small></div><span class="token-status active">Configured</span><div class="token-actions"><button class="button token-action stop" type="button" id="clear-context7-key" ' + (context7.environmentOverride ? 'disabled' : '') + '>Remove</button></div></div>';
|
|
608
|
+
} else {
|
|
609
|
+
context7KeyStatus.innerHTML = '<div class="empty">No Context7 API key saved.</div>';
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function renderConnection() {
|
|
614
|
+
const local = document.getElementById("local-mcp");
|
|
615
|
+
const publicUrl = document.getElementById("public-mcp");
|
|
616
|
+
local.textContent = state.localMcpUrl;
|
|
617
|
+
local.href = state.localMcpUrl;
|
|
618
|
+
publicUrl.textContent = state.publicMcpUrl;
|
|
619
|
+
publicUrl.href = state.publicMcpUrl;
|
|
620
|
+
const root = state.allowedRoots[0] || "No workspace configured";
|
|
621
|
+
const workspace = document.getElementById("workspace");
|
|
622
|
+
workspace.textContent = root + (state.allowedRoots.length > 1 ? " +" + (state.allowedRoots.length - 1) : "");
|
|
623
|
+
workspace.title = state.allowedRoots.join("\\n");
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function renderServerState() {
|
|
627
|
+
const title = document.getElementById("server-state-title");
|
|
628
|
+
const detail = document.getElementById("server-state-detail");
|
|
629
|
+
const dot = document.getElementById("server-state-dot");
|
|
630
|
+
if (lifecycleMode === "restarting") { title.textContent = "Restarting"; detail.textContent = "Waiting for Auvrynt to return"; dot.style.background = "var(--auvrynt-warning)"; return; }
|
|
631
|
+
if (lifecycleMode === "stopped") { title.textContent = "Stopped"; detail.textContent = "Auvrynt is offline"; dot.style.background = "var(--auvrynt-danger)"; return; }
|
|
632
|
+
if (lifecycleMode === "stopping" || !state.ready) { title.textContent = "Stopping"; detail.textContent = "Closing local services"; dot.style.background = "var(--auvrynt-warning)"; return; }
|
|
633
|
+
title.textContent = "Server online";
|
|
634
|
+
detail.textContent = "Refreshes every 2 seconds";
|
|
635
|
+
dot.style.background = "var(--auvrynt-accent)";
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function renderState(nextState) {
|
|
639
|
+
const previousNewestId = knownNewestId;
|
|
640
|
+
state = nextState;
|
|
641
|
+
knownNewestId = state.logs[0]?.id ?? previousNewestId;
|
|
642
|
+
if (knownNewestId > previousNewestId && logs.scrollTop > 4) unseenLogs += state.logs.filter((entry) => entry.id > previousNewestId).length;
|
|
643
|
+
renderAgentPresence();
|
|
644
|
+
renderConnection();
|
|
645
|
+
renderIntegrations();
|
|
646
|
+
renderSkills();
|
|
647
|
+
renderNgrok();
|
|
648
|
+
renderContext7();
|
|
649
|
+
renderAnalytics();
|
|
650
|
+
renderLogs();
|
|
651
|
+
renderServerState();
|
|
652
|
+
if (activeView === "tools") {
|
|
653
|
+
renderToolsTabs();
|
|
654
|
+
if (toolsData) renderTools();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async function refresh() {
|
|
659
|
+
if (lifecycleMode) return;
|
|
660
|
+
try {
|
|
661
|
+
const response = await fetch("/dashboard/data", {
|
|
662
|
+
cache: "no-store",
|
|
663
|
+
signal: AbortSignal.timeout(3_000),
|
|
664
|
+
});
|
|
665
|
+
if (!response.ok) throw new Error("Dashboard refresh failed (" + response.status + ")");
|
|
666
|
+
const nextState = await response.json();
|
|
667
|
+
refreshFailures = 0;
|
|
668
|
+
renderState(nextState);
|
|
669
|
+
} catch {
|
|
670
|
+
refreshFailures += 1;
|
|
671
|
+
if (refreshFailures >= 3) {
|
|
672
|
+
const title = document.getElementById("server-state-title");
|
|
673
|
+
const detail = document.getElementById("server-state-detail");
|
|
674
|
+
const dot = document.getElementById("server-state-dot");
|
|
675
|
+
title.textContent = "Reconnecting";
|
|
676
|
+
detail.textContent = "Waiting for dashboard data";
|
|
677
|
+
dot.style.background = "var(--auvrynt-warning)";
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
async function postAction(path, body) {
|
|
683
|
+
const response = await fetch(path, {
|
|
684
|
+
method: "POST",
|
|
685
|
+
headers: { "content-type": "application/json", "x-auvrynt-dashboard": "1" },
|
|
686
|
+
body: JSON.stringify(body ?? {}),
|
|
687
|
+
});
|
|
688
|
+
const result = await response.json().catch(() => ({}));
|
|
689
|
+
if (!response.ok) throw new Error(result.error || "Action failed (" + response.status + ")");
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function setButtonBusy(button, busy, label) {
|
|
694
|
+
if (!button.dataset.label) button.dataset.label = button.textContent;
|
|
695
|
+
button.setAttribute("aria-busy", String(busy));
|
|
696
|
+
button.disabled = busy;
|
|
697
|
+
button.textContent = busy ? label : button.dataset.label;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function beginLifecycle(mode, message, detail) {
|
|
701
|
+
lifecycleMode = mode;
|
|
702
|
+
if (refreshTimer) window.clearInterval(refreshTimer);
|
|
703
|
+
restartButton.disabled = true;
|
|
704
|
+
stopButton.disabled = true;
|
|
705
|
+
document.documentElement.setAttribute("aria-busy", "true");
|
|
706
|
+
lifecycleOverlay.classList.remove("complete");
|
|
707
|
+
lifecycleTitle.textContent = mode === "restarting" ? "Restarting Auvrynt" : "Stopping Auvrynt";
|
|
708
|
+
lifecycleMessage.textContent = detail || (mode === "restarting"
|
|
709
|
+
? "Please wait while the dashboard reconnects."
|
|
710
|
+
: "Please wait while active connections and local services close safely.");
|
|
711
|
+
lifecycleOverlay.classList.add("active");
|
|
712
|
+
lifecycleOverlay.setAttribute("aria-hidden", "false");
|
|
713
|
+
state = { ...state, ready: false, agentState: "stopping" };
|
|
714
|
+
showToast(message, "info", 0);
|
|
715
|
+
renderAgentPresence();
|
|
716
|
+
renderServerState();
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function endLifecycle() {
|
|
720
|
+
lifecycleMode = null;
|
|
721
|
+
document.documentElement.removeAttribute("aria-busy");
|
|
722
|
+
lifecycleOverlay.classList.remove("active", "complete");
|
|
723
|
+
lifecycleOverlay.setAttribute("aria-hidden", "true");
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function finishStopLifecycle() {
|
|
727
|
+
lifecycleMode = "stopped";
|
|
728
|
+
document.documentElement.removeAttribute("aria-busy");
|
|
729
|
+
lifecycleOverlay.classList.add("complete");
|
|
730
|
+
lifecycleTitle.textContent = "Auvrynt stopped";
|
|
731
|
+
lifecycleMessage.textContent = "The local server and tunnel are offline. Run auvrynt start when you are ready to reconnect.";
|
|
732
|
+
setButtonBusy(stopButton, false, "");
|
|
733
|
+
restartButton.disabled = true;
|
|
734
|
+
stopButton.disabled = true;
|
|
735
|
+
renderServerState();
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async function waitForRestart() {
|
|
739
|
+
const previousPid = state.pid;
|
|
740
|
+
const previousPublicMcpUrl = state.publicMcpUrl;
|
|
741
|
+
let sawOffline = false;
|
|
742
|
+
const deadline = Date.now() + 90_000;
|
|
743
|
+
let attempt = 0;
|
|
744
|
+
while (Date.now() < deadline) {
|
|
745
|
+
attempt += 1;
|
|
746
|
+
await delay(Math.min(500 + attempt * 75, 1_500));
|
|
747
|
+
try {
|
|
748
|
+
const response = await fetch("/healthz?restart=" + Date.now(), {
|
|
749
|
+
cache: "no-store",
|
|
750
|
+
signal: AbortSignal.timeout(3_000),
|
|
751
|
+
});
|
|
752
|
+
if (!response.ok) { sawOffline = true; continue; }
|
|
753
|
+
const health = await response.json().catch(() => ({}));
|
|
754
|
+
const pidChanged = typeof health.pid === "number" && health.pid !== previousPid;
|
|
755
|
+
if (sawOffline || pidChanged) {
|
|
756
|
+
const dashboardResponse = await fetch("/dashboard/data?restart=" + Date.now(), {
|
|
757
|
+
cache: "no-store",
|
|
758
|
+
signal: AbortSignal.timeout(3_000),
|
|
759
|
+
});
|
|
760
|
+
if (!dashboardResponse.ok) continue;
|
|
761
|
+
const nextState = await dashboardResponse.json();
|
|
762
|
+
const publicUrlChanged = Boolean(
|
|
763
|
+
previousPublicMcpUrl
|
|
764
|
+
&& nextState.publicMcpUrl
|
|
765
|
+
&& previousPublicMcpUrl !== nextState.publicMcpUrl,
|
|
766
|
+
);
|
|
767
|
+
renderState(nextState);
|
|
768
|
+
endLifecycle();
|
|
769
|
+
restartButton.disabled = false;
|
|
770
|
+
stopButton.disabled = false;
|
|
771
|
+
setButtonBusy(restartButton, false, "");
|
|
772
|
+
refreshTimer = window.setInterval(refresh, 2_000);
|
|
773
|
+
showToast(
|
|
774
|
+
publicUrlChanged
|
|
775
|
+
? "Auvrynt restarted, but the public MCP URL changed. Update the connector to the new URL shown in Connectivity."
|
|
776
|
+
: "Auvrynt restarted successfully.",
|
|
777
|
+
publicUrlChanged ? "error" : "info",
|
|
778
|
+
publicUrlChanged ? 0 : 2400,
|
|
779
|
+
);
|
|
780
|
+
if (publicUrlChanged) setActiveView("connectivity");
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
} catch {
|
|
784
|
+
sawOffline = true;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
endLifecycle();
|
|
788
|
+
restartButton.disabled = false;
|
|
789
|
+
stopButton.disabled = false;
|
|
790
|
+
setButtonBusy(restartButton, false, "");
|
|
791
|
+
showToast("Restart did not complete. The existing public URL was preserved; check Auvrynt status, then retry.", "error", 0);
|
|
792
|
+
refreshTimer = window.setInterval(refresh, 2_000);
|
|
793
|
+
void refresh();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
async function waitForStop() {
|
|
797
|
+
const deadline = Date.now() + 30_000;
|
|
798
|
+
let consecutiveOfflineChecks = 0;
|
|
799
|
+
while (Date.now() < deadline) {
|
|
800
|
+
await delay(650);
|
|
801
|
+
try {
|
|
802
|
+
const response = await fetch("/healthz?stop=" + Date.now(), {
|
|
803
|
+
cache: "no-store",
|
|
804
|
+
signal: AbortSignal.timeout(2_000),
|
|
805
|
+
});
|
|
806
|
+
consecutiveOfflineChecks = response.ok ? 0 : consecutiveOfflineChecks + 1;
|
|
807
|
+
} catch {
|
|
808
|
+
consecutiveOfflineChecks++;
|
|
809
|
+
}
|
|
810
|
+
if (consecutiveOfflineChecks >= 2) {
|
|
811
|
+
finishStopLifecycle();
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
endLifecycle();
|
|
816
|
+
restartButton.disabled = false;
|
|
817
|
+
stopButton.disabled = false;
|
|
818
|
+
setButtonBusy(stopButton, false, "");
|
|
819
|
+
showToast("Auvrynt did not finish stopping within 30 seconds. Check its status and retry.", "error", 0);
|
|
820
|
+
refreshTimer = window.setInterval(refresh, 2_000);
|
|
821
|
+
void refresh();
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
document.querySelectorAll("[data-view]").forEach((tab) => {
|
|
825
|
+
tab.addEventListener("click", () => setActiveView(tab.dataset.view));
|
|
826
|
+
tab.addEventListener("keydown", (event) => {
|
|
827
|
+
const horizontal = window.matchMedia("(max-width: 940px)").matches;
|
|
828
|
+
const previousKey = horizontal ? "ArrowLeft" : "ArrowUp";
|
|
829
|
+
const nextKey = horizontal ? "ArrowRight" : "ArrowDown";
|
|
830
|
+
if (event.key !== previousKey && event.key !== nextKey) return;
|
|
831
|
+
event.preventDefault();
|
|
832
|
+
const direction = event.key === nextKey ? 1 : -1;
|
|
833
|
+
const nextIndex = (viewNames.indexOf(activeView) + direction + viewNames.length) % viewNames.length;
|
|
834
|
+
setActiveView(viewNames[nextIndex]);
|
|
835
|
+
document.querySelector('[data-view="' + activeView + '"]').focus();
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
window.addEventListener("hashchange", () => setActiveView(window.location.hash.slice(1), false));
|
|
839
|
+
openSecretsButton.addEventListener("click", () => setActiveView("secrets"));
|
|
840
|
+
|
|
841
|
+
confirmCancel.addEventListener("click", () => finishConfirmation(false));
|
|
842
|
+
confirmAccept.addEventListener("click", () => finishConfirmation(true));
|
|
843
|
+
confirmDialog.addEventListener("cancel", (event) => { event.preventDefault(); finishConfirmation(false); });
|
|
844
|
+
confirmDialog.addEventListener("click", (event) => {
|
|
845
|
+
if (event.target === confirmDialog) finishConfirmation(false);
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
openToolSettingsButton?.addEventListener("click", () => { void openToolsView(); });
|
|
849
|
+
|
|
850
|
+
integrations.addEventListener("click", async (event) => {
|
|
851
|
+
const button = event.target.closest("[data-integration]");
|
|
852
|
+
if (!button) return;
|
|
853
|
+
const enabled = button.dataset.enabled === "true";
|
|
854
|
+
const label = button.closest(".integration-row")?.querySelector("strong")?.textContent || "integration";
|
|
855
|
+
if (enabled && !await confirmAction("Disable " + label + "?", "Active operations for this integration may be interrupted.", "Disable")) return;
|
|
856
|
+
setButtonBusy(button, true, enabled ? "Disabling" : "Enabling");
|
|
857
|
+
try {
|
|
858
|
+
const result = await postAction("/__auvrynt/dashboard/integrations", { integration: button.dataset.integration, enabled: !enabled });
|
|
859
|
+
showToast(result.message || "Integration updated.");
|
|
860
|
+
await refresh();
|
|
861
|
+
} catch (error) {
|
|
862
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
863
|
+
setButtonBusy(button, false, "");
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
async function loadToolsData(integration) {
|
|
868
|
+
const response = await fetch("/dashboard/tools?integration=" + encodeURIComponent(integration), { cache: "no-store", signal: AbortSignal.timeout(5_000) });
|
|
869
|
+
if (!response.ok) throw new Error("Tool data failed (" + response.status + ")");
|
|
870
|
+
return await response.json();
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function integrationByKey(key) {
|
|
874
|
+
return state.integrations.find((item) => item.key === key);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function buildToolsPrimaryTabs() {
|
|
878
|
+
const tabs = [];
|
|
879
|
+
const gds = integrationByKey("godotGdscript");
|
|
880
|
+
const gcs = integrationByKey("godotCsharp");
|
|
881
|
+
const godotCount = (gds?.toolCount || 0) + (gcs?.toolCount || 0);
|
|
882
|
+
if (godotCount > 0) {
|
|
883
|
+
tabs.push({ key: "godot", label: "Godot", toolCount: godotCount });
|
|
884
|
+
}
|
|
885
|
+
const serena = integrationByKey("serena");
|
|
886
|
+
const context7 = integrationByKey("context7");
|
|
887
|
+
const obsidian = integrationByKey("obsidian");
|
|
888
|
+
const memoryCount = (serena?.toolCount || 0) + (context7?.toolCount || 0) + (obsidian?.toolCount || 0);
|
|
889
|
+
if (memoryCount > 0) {
|
|
890
|
+
tabs.push({ key: "memory", label: "Memory", toolCount: memoryCount });
|
|
891
|
+
}
|
|
892
|
+
for (const item of state.integrations) {
|
|
893
|
+
if (item.key === "godotGdscript" || item.key === "godotCsharp" || item.key === "serena" || item.key === "context7" || item.key === "obsidian") continue;
|
|
894
|
+
if (item.toolCount > 0) tabs.push({ key: item.key, label: item.label, toolCount: item.toolCount });
|
|
895
|
+
}
|
|
896
|
+
return tabs;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function buildToolsSubTabs(primary) {
|
|
900
|
+
if (primary === "godot") {
|
|
901
|
+
return [
|
|
902
|
+
{ key: "godotGdscript", label: "GDScript", toolCount: integrationByKey("godotGdscript")?.toolCount || 0 },
|
|
903
|
+
{ key: "godotCsharp", label: "C#", toolCount: integrationByKey("godotCsharp")?.toolCount || 0 },
|
|
904
|
+
].filter((item) => item.toolCount > 0);
|
|
905
|
+
}
|
|
906
|
+
if (primary === "memory") {
|
|
907
|
+
return [
|
|
908
|
+
{ key: "serena", label: "Serena", toolCount: integrationByKey("serena")?.toolCount || 0 },
|
|
909
|
+
{ key: "obsidian", label: "Obsidian", toolCount: integrationByKey("obsidian")?.toolCount || 0 },
|
|
910
|
+
{ key: "context7", label: "Context7", toolCount: integrationByKey("context7")?.toolCount || 0 },
|
|
911
|
+
];
|
|
912
|
+
}
|
|
913
|
+
return [];
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
function resolveToolsSelection(preferredIntegration) {
|
|
917
|
+
const primaryTabs = buildToolsPrimaryTabs();
|
|
918
|
+
if (!primaryTabs.length) {
|
|
919
|
+
toolsPrimary = null;
|
|
920
|
+
toolsSub = null;
|
|
921
|
+
toolsIntegration = null;
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (preferredIntegration === "godotGdscript" || preferredIntegration === "godotCsharp") {
|
|
925
|
+
toolsPrimary = primaryTabs.some((t) => t.key === "godot") ? "godot" : primaryTabs[0].key;
|
|
926
|
+
toolsSub = preferredIntegration;
|
|
927
|
+
} else if (preferredIntegration === "serena") {
|
|
928
|
+
toolsPrimary = primaryTabs.some((t) => t.key === "memory") ? "memory" : primaryTabs[0].key;
|
|
929
|
+
toolsSub = "serena";
|
|
930
|
+
} else if (preferredIntegration && primaryTabs.some((t) => t.key === preferredIntegration)) {
|
|
931
|
+
toolsPrimary = preferredIntegration;
|
|
932
|
+
toolsSub = null;
|
|
933
|
+
} else if (!toolsPrimary || !primaryTabs.some((t) => t.key === toolsPrimary)) {
|
|
934
|
+
const enabled = state.integrations.find((item) => item.enabled && item.toolCount > 0);
|
|
935
|
+
if (enabled?.key === "godotGdscript" || enabled?.key === "godotCsharp") {
|
|
936
|
+
toolsPrimary = "godot";
|
|
937
|
+
toolsSub = enabled.key;
|
|
938
|
+
} else if (enabled?.key === "serena") {
|
|
939
|
+
toolsPrimary = "memory";
|
|
940
|
+
toolsSub = "serena";
|
|
941
|
+
} else {
|
|
942
|
+
toolsPrimary = enabled?.key || primaryTabs[0].key;
|
|
943
|
+
toolsSub = null;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
const subs = buildToolsSubTabs(toolsPrimary);
|
|
947
|
+
if (subs.length) {
|
|
948
|
+
if (!toolsSub || !subs.some((s) => s.key === toolsSub)) {
|
|
949
|
+
toolsSub = subs.find((s) => s.toolCount > 0)?.key || subs[0].key;
|
|
950
|
+
}
|
|
951
|
+
toolsIntegration = toolsSub;
|
|
952
|
+
} else {
|
|
953
|
+
toolsSub = null;
|
|
954
|
+
toolsIntegration = toolsPrimary;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
async function loadActiveToolsPanel() {
|
|
959
|
+
if (!toolsPrimary) {
|
|
960
|
+
toolsData = null;
|
|
961
|
+
toolsLayout.innerHTML = '<div class="empty">No integration tools are registered yet.</div>';
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
if (!toolsIntegration) {
|
|
965
|
+
toolsData = null;
|
|
966
|
+
toolsLayout.innerHTML = '<div class="empty">No tools available for this tab.</div>';
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
toolsLayout.innerHTML = '<div class="empty">Loading tools…</div>';
|
|
970
|
+
try {
|
|
971
|
+
const data = await loadToolsData(toolsIntegration);
|
|
972
|
+
toolsData = data.tools || [];
|
|
973
|
+
renderTools();
|
|
974
|
+
} catch (error) {
|
|
975
|
+
toolsData = null;
|
|
976
|
+
toolsLayout.innerHTML = '<div class="empty">Unable to load tools: ' + escape(error instanceof Error ? error.message : String(error)) + "</div>";
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
async function openToolsView(preferredIntegration) {
|
|
981
|
+
toolsPrimary = null;
|
|
982
|
+
toolsSub = null;
|
|
983
|
+
toolsIntegration = null;
|
|
984
|
+
toolsData = null;
|
|
985
|
+
resolveToolsSelection(preferredIntegration);
|
|
986
|
+
setActiveView("tools");
|
|
987
|
+
await loadActiveToolsPanel();
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function renderToolsTabs() {
|
|
991
|
+
const primaryTabs = buildToolsPrimaryTabs();
|
|
992
|
+
toolsIntegrationTabs.innerHTML = primaryTabs.map((item) => {
|
|
993
|
+
const active = item.key === toolsPrimary;
|
|
994
|
+
return '<button class="tools-tab' + (active ? " active" : "") + '" role="tab" aria-selected="' + (active ? "true" : "false") + '" data-tools-tab="' + escape(item.key) + '" type="button">' + escape(item.label) + '<small>' + item.toolCount + '</small></button>';
|
|
995
|
+
}).join("") || '<span class="tools-no-tabs">No integration tools are registered yet.</span>';
|
|
996
|
+
|
|
997
|
+
const subs = buildToolsSubTabs(toolsPrimary);
|
|
998
|
+
if (!toolsSubTabs) return;
|
|
999
|
+
if (!subs.length) {
|
|
1000
|
+
toolsSubTabs.hidden = true;
|
|
1001
|
+
toolsSubTabs.innerHTML = "";
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
toolsSubTabs.hidden = false;
|
|
1005
|
+
toolsSubTabs.innerHTML = subs.map((item) => {
|
|
1006
|
+
const active = item.key === toolsSub;
|
|
1007
|
+
const count = item.placeholder ? "" : "<small>" + item.toolCount + "</small>";
|
|
1008
|
+
return '<button class="tools-tab' + (active ? " active" : "") + '" role="tab" aria-selected="' + (active ? "true" : "false") + '" data-tools-sub="' + escape(item.key) + '" type="button">' + escape(item.label) + count + '</button>';
|
|
1009
|
+
}).join("");
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
function captureOpenToolBatches() {
|
|
1014
|
+
toolsLayout.querySelectorAll("[data-tools-batch]").forEach((batch) => {
|
|
1015
|
+
if (batch.open) toolsOpenGroups.add(batch.dataset.toolsBatch);
|
|
1016
|
+
else toolsOpenGroups.delete(batch.dataset.toolsBatch);
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function renderTools() {
|
|
1021
|
+
if (!toolsData) { toolsLayout.innerHTML = '<div class="empty">No tools loaded.</div>'; return; }
|
|
1022
|
+
captureOpenToolBatches();
|
|
1023
|
+
const filter = toolsFilter.trim().toLowerCase();
|
|
1024
|
+
const visible = filter ? toolsData.filter((tool) => tool.name.toLowerCase().includes(filter) || tool.title.toLowerCase().includes(filter) || tool.group.toLowerCase().includes(filter)) : toolsData;
|
|
1025
|
+
const groups = new Map();
|
|
1026
|
+
for (const tool of visible) {
|
|
1027
|
+
const list = groups.get(tool.group) ?? [];
|
|
1028
|
+
list.push(tool);
|
|
1029
|
+
groups.set(tool.group, list);
|
|
1030
|
+
}
|
|
1031
|
+
const allEnabledCount = toolsData.filter((tool) => tool.enabled).length;
|
|
1032
|
+
const total = toolsData.length;
|
|
1033
|
+
const summary = '<div class="tools-summary"><strong>' + allEnabledCount + '</strong> of <strong>' + total + '</strong> tools enabled</div>';
|
|
1034
|
+
const searching = Boolean(toolsFilter.trim());
|
|
1035
|
+
const groupMarkup = Array.from(groups.entries()).map(([group, tools]) => {
|
|
1036
|
+
const enabledCount = tools.filter((tool) => tool.enabled).length;
|
|
1037
|
+
const allOn = enabledCount === tools.length;
|
|
1038
|
+
const someOn = enabledCount > 0 && !allOn;
|
|
1039
|
+
const open = searching || toolsOpenGroups.has(group);
|
|
1040
|
+
const groupBusy = tools.some((tool) => toolsBusy.has(tool.name));
|
|
1041
|
+
return '<details class="tools-group' + (someOn ? " partial" : "") + '" data-tools-batch="' + escape(group) + '"' + (open ? " open" : "") + '>' +
|
|
1042
|
+
'<summary class="tools-group-head">' +
|
|
1043
|
+
'<span class="tools-group-title"><span class="tools-group-chevron" aria-hidden="true"></span><strong>' + escape(group) + '</strong><span class="tools-group-count">' + enabledCount + '/' + tools.length + '</span></span>' +
|
|
1044
|
+
'<label class="tool-toggle tools-group-toggle" title="' + (allOn ? "Disable batch" : "Enable batch") + '"' + (someOn ? ' data-partial="true"' : '') + '>' +
|
|
1045
|
+
'<input type="checkbox" data-tools-group="' + escape(group) + '"' + (allOn ? " checked" : "") + (groupBusy ? " disabled" : "") + ' />' +
|
|
1046
|
+
'<span class="tool-toggle-track" aria-hidden="true"></span>' +
|
|
1047
|
+
'</label>' +
|
|
1048
|
+
'</summary>' +
|
|
1049
|
+
'<div class="tools-group-list">' + tools.map((tool) => {
|
|
1050
|
+
const busy = toolsBusy.has(tool.name);
|
|
1051
|
+
return '<div class="tool-row' + (tool.enabled ? "" : " disabled") + '"><label class="tool-check"><input type="checkbox" data-tool-name="' + escape(tool.name) + '"' + (tool.enabled ? " checked" : "") + (busy ? " disabled" : "") + ' /><span class="tool-check-box" aria-hidden="true"></span></label><div class="tool-info"><strong>' + escape(tool.title) + '</strong><code>' + escape(tool.name) + '</code></div></div>';
|
|
1052
|
+
}).join("") + '</div></details>';
|
|
1053
|
+
}).join("");
|
|
1054
|
+
toolsRendering = true;
|
|
1055
|
+
toolsLayout.innerHTML = summary + (groupMarkup || '<div class="empty">No tools match the filter.</div>');
|
|
1056
|
+
toolsRendering = false;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
toolsIntegrationTabs.addEventListener("click", (event) => {
|
|
1060
|
+
const tab = event.target.closest("[data-tools-tab]");
|
|
1061
|
+
if (!tab) return;
|
|
1062
|
+
toolsPrimary = tab.dataset.toolsTab;
|
|
1063
|
+
const subs = buildToolsSubTabs(toolsPrimary);
|
|
1064
|
+
toolsSub = subs.find((s) => s.toolCount > 0)?.key || subs[0]?.key || null;
|
|
1065
|
+
toolsIntegration = toolsSub || toolsPrimary;
|
|
1066
|
+
renderToolsTabs();
|
|
1067
|
+
void loadActiveToolsPanel();
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
toolsSubTabs?.addEventListener("click", (event) => {
|
|
1071
|
+
const tab = event.target.closest("[data-tools-sub]");
|
|
1072
|
+
if (!tab) return;
|
|
1073
|
+
toolsSub = tab.dataset.toolsSub;
|
|
1074
|
+
toolsIntegration = toolsSub;
|
|
1075
|
+
renderToolsTabs();
|
|
1076
|
+
void loadActiveToolsPanel();
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
toolsSearch.addEventListener("input", () => {
|
|
1080
|
+
toolsFilter = toolsSearch.value;
|
|
1081
|
+
renderTools();
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
toolsLayout.addEventListener("toggle", (event) => {
|
|
1085
|
+
if (toolsRendering) return;
|
|
1086
|
+
const batch = event.target.closest("[data-tools-batch]");
|
|
1087
|
+
if (!batch || event.target !== batch) return;
|
|
1088
|
+
if (batch.open) toolsOpenGroups.add(batch.dataset.toolsBatch);
|
|
1089
|
+
else toolsOpenGroups.delete(batch.dataset.toolsBatch);
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
toolsLayout.addEventListener("click", (event) => {
|
|
1093
|
+
if (event.target.closest(".tools-group-toggle")) event.stopPropagation();
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
toolsLayout.addEventListener("change", async (event) => {
|
|
1097
|
+
const groupToggle = event.target.closest("[data-tools-group]");
|
|
1098
|
+
if (groupToggle) {
|
|
1099
|
+
const group = groupToggle.dataset.toolsGroup;
|
|
1100
|
+
const groupEnabled = groupToggle.checked;
|
|
1101
|
+
const names = toolsData.filter((tool) => tool.group === group).map((tool) => tool.name);
|
|
1102
|
+
for (const name of names) toolsBusy.add(name);
|
|
1103
|
+
groupToggle.disabled = true;
|
|
1104
|
+
try {
|
|
1105
|
+
const result = await postAction("/__auvrynt/dashboard/tools", { integration: toolsIntegration, group, groupEnabled });
|
|
1106
|
+
for (const tool of toolsData) if (tool.group === group) tool.enabled = groupEnabled;
|
|
1107
|
+
toolsOpenGroups.add(group);
|
|
1108
|
+
renderTools();
|
|
1109
|
+
renderToolsTabs();
|
|
1110
|
+
showToast(result.message || (groupEnabled ? group + " tools enabled." : group + " tools disabled."));
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
groupToggle.checked = !groupEnabled;
|
|
1113
|
+
toolsOpenGroups.add(group);
|
|
1114
|
+
renderTools();
|
|
1115
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1116
|
+
} finally {
|
|
1117
|
+
for (const name of names) toolsBusy.delete(name);
|
|
1118
|
+
}
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
const checkbox = event.target.closest("[data-tool-name]");
|
|
1122
|
+
if (!checkbox) return;
|
|
1123
|
+
const name = checkbox.dataset.toolName;
|
|
1124
|
+
const enabled = checkbox.checked;
|
|
1125
|
+
toolsBusy.add(name);
|
|
1126
|
+
checkbox.disabled = true;
|
|
1127
|
+
try {
|
|
1128
|
+
const result = await postAction("/__auvrynt/dashboard/tools", { integration: toolsIntegration, name, enabled });
|
|
1129
|
+
const tool = toolsData.find((item) => item.name === name);
|
|
1130
|
+
if (tool) {
|
|
1131
|
+
tool.enabled = enabled;
|
|
1132
|
+
toolsOpenGroups.add(tool.group);
|
|
1133
|
+
}
|
|
1134
|
+
renderTools();
|
|
1135
|
+
renderToolsTabs();
|
|
1136
|
+
showToast(result.message || (enabled ? name + " enabled." : name + " disabled."));
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
checkbox.checked = !enabled;
|
|
1139
|
+
const tool = toolsData.find((item) => item.name === name);
|
|
1140
|
+
if (tool) toolsOpenGroups.add(tool.group);
|
|
1141
|
+
renderTools();
|
|
1142
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1143
|
+
} finally {
|
|
1144
|
+
toolsBusy.delete(name);
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
function applyNgrokResult(result) {
|
|
1149
|
+
state.ngrok = result.ngrok;
|
|
1150
|
+
renderNgrok();
|
|
1151
|
+
renderContext7();
|
|
1152
|
+
showToast(result.message || "ngrok credentials updated.");
|
|
1153
|
+
if (result.restarting) {
|
|
1154
|
+
beginLifecycle(
|
|
1155
|
+
"restarting",
|
|
1156
|
+
result.message || "Restarting Auvrynt with the selected ngrok token.",
|
|
1157
|
+
"The ngrok tunnel is being recreated. A random ngrok URL may change, and the connector may need the new URL.",
|
|
1158
|
+
);
|
|
1159
|
+
void waitForRestart();
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function applyContext7Result(result) {
|
|
1166
|
+
state.context7 = result.context7;
|
|
1167
|
+
renderContext7();
|
|
1168
|
+
showToast(result.message || "Context7 credentials updated.");
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
context7KeyForm.addEventListener("submit", async (event) => {
|
|
1172
|
+
event.preventDefault();
|
|
1173
|
+
const apiKey = context7KeyInput.value.trim();
|
|
1174
|
+
if (apiKey.length < 8 || apiKey.length > 512 || /\s/.test(apiKey)) {
|
|
1175
|
+
showToast("Enter a valid Context7 API key without spaces.", "error");
|
|
1176
|
+
context7KeyInput.focus();
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
setButtonBusy(saveContext7KeyButton, true, "Saving");
|
|
1180
|
+
context7KeyInput.disabled = true;
|
|
1181
|
+
try {
|
|
1182
|
+
const result = await postAction("/__auvrynt/dashboard/context7-key", { action: "set", apiKey });
|
|
1183
|
+
context7KeyInput.value = "";
|
|
1184
|
+
applyContext7Result(result);
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1187
|
+
} finally {
|
|
1188
|
+
context7KeyInput.value = "";
|
|
1189
|
+
if (!lifecycleMode) {
|
|
1190
|
+
context7KeyInput.disabled = Boolean(state.context7?.environmentOverride);
|
|
1191
|
+
setButtonBusy(saveContext7KeyButton, false, "");
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
context7KeyStatus.addEventListener("click", async (event) => {
|
|
1197
|
+
const button = event.target.closest("#clear-context7-key");
|
|
1198
|
+
if (!button || button.disabled) return;
|
|
1199
|
+
if (!await confirmAction("Remove Context7 API key?", "Auvrynt will delete the saved Context7 API key from the local auth file.", "Remove")) return;
|
|
1200
|
+
setButtonBusy(button, true, "Removing");
|
|
1201
|
+
try {
|
|
1202
|
+
const result = await postAction("/__auvrynt/dashboard/context7-key", { action: "clear" });
|
|
1203
|
+
applyContext7Result(result);
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1206
|
+
} finally {
|
|
1207
|
+
if (!lifecycleMode) setButtonBusy(button, false, "");
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
ngrokTokenForm.addEventListener("submit", async (event) => {
|
|
1212
|
+
event.preventDefault();
|
|
1213
|
+
const token = ngrokTokenInput.value.trim();
|
|
1214
|
+
if (token.length < 16 || token.length > 512 || /\\s/.test(token)) {
|
|
1215
|
+
showToast("Enter a valid ngrok authtoken without spaces.", "error");
|
|
1216
|
+
ngrokTokenInput.focus();
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
setButtonBusy(addNgrokTokenButton, true, "Adding");
|
|
1220
|
+
ngrokTokenInput.disabled = true;
|
|
1221
|
+
try {
|
|
1222
|
+
const result = await postAction("/__auvrynt/dashboard/ngrok-tokens", { action: "add", token });
|
|
1223
|
+
ngrokTokenInput.value = "";
|
|
1224
|
+
applyNgrokResult(result);
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1227
|
+
} finally {
|
|
1228
|
+
ngrokTokenInput.value = "";
|
|
1229
|
+
if (!lifecycleMode) {
|
|
1230
|
+
ngrokTokenInput.disabled = false;
|
|
1231
|
+
setButtonBusy(addNgrokTokenButton, false, "");
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
copyNgrokSecretButton.addEventListener("click", async () => {
|
|
1237
|
+
setButtonBusy(copyNgrokSecretButton, true, "Copying");
|
|
1238
|
+
try {
|
|
1239
|
+
const result = await postAction("/__auvrynt/dashboard/ngrok-tokens", { action: "copy" });
|
|
1240
|
+
await copyText(result.token, copyNgrokSecretButton, "ngrok secret copied.");
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1243
|
+
} finally {
|
|
1244
|
+
if (!lifecycleMode) setButtonBusy(copyNgrokSecretButton, false, "");
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
ngrokTokenList.addEventListener("click", async (event) => {
|
|
1249
|
+
const button = event.target.closest("[data-ngrok-action]");
|
|
1250
|
+
if (!button) return;
|
|
1251
|
+
const action = button.dataset.ngrokAction;
|
|
1252
|
+
const index = Number(button.dataset.ngrokIndex);
|
|
1253
|
+
const token = state.ngrok.tokens.find((item) => item.index === index);
|
|
1254
|
+
if (!token) return;
|
|
1255
|
+
if (action === "remove" && !await confirmAction("Remove ngrok token?", "Auvrynt will permanently delete saved token " + token.fingerprint + ".", "Remove")) return;
|
|
1256
|
+
if (action === "activate" && !await confirmAction("Switch ngrok token?", "Auvrynt will recreate the public tunnel using token " + token.fingerprint + ".", "Switch")) return;
|
|
1257
|
+
setButtonBusy(button, true, action === "remove" ? "Removing" : "Switching");
|
|
1258
|
+
try {
|
|
1259
|
+
const result = await postAction("/__auvrynt/dashboard/ngrok-tokens", { action, index });
|
|
1260
|
+
if (!applyNgrokResult(result)) setButtonBusy(button, false, "");
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1263
|
+
setButtonBusy(button, false, "");
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
restartButton.addEventListener("click", async () => {
|
|
1268
|
+
if (!await confirmAction("Restart Auvrynt?", "Connected AI clients may briefly lose access while the server restarts.", "Restart")) return;
|
|
1269
|
+
setButtonBusy(restartButton, true, "Restarting");
|
|
1270
|
+
try {
|
|
1271
|
+
const result = await postAction("/__auvrynt/dashboard/restart", {});
|
|
1272
|
+
beginLifecycle("restarting", result.message || "Restarting Auvrynt… The public MCP URL will stay the same.");
|
|
1273
|
+
void waitForRestart();
|
|
1274
|
+
} catch (error) {
|
|
1275
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1276
|
+
setButtonBusy(restartButton, false, "");
|
|
1277
|
+
stopButton.disabled = false;
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
stopButton.addEventListener("click", async () => {
|
|
1282
|
+
if (!await confirmAction("Stop Auvrynt?", "Connected AI clients will lose access until Auvrynt is started again.", "Stop")) return;
|
|
1283
|
+
setButtonBusy(stopButton, true, "Stopping");
|
|
1284
|
+
try {
|
|
1285
|
+
const result = await postAction("/__auvrynt/dashboard/stop", {});
|
|
1286
|
+
beginLifecycle("stopping", result.message || "Auvrynt is stopping…");
|
|
1287
|
+
void waitForStop();
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1290
|
+
setButtonBusy(stopButton, false, "");
|
|
1291
|
+
restartButton.disabled = false;
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
const editWorkspaceButton = document.getElementById("edit-workspace");
|
|
1296
|
+
editWorkspaceButton.addEventListener("click", async () => {
|
|
1297
|
+
editWorkspaceButton.disabled = true;
|
|
1298
|
+
editWorkspaceButton.setAttribute("aria-busy", "true");
|
|
1299
|
+
try {
|
|
1300
|
+
const selected = await postAction("/__auvrynt/dashboard/select-workspace", {});
|
|
1301
|
+
if (selected.canceled || !selected.path) return;
|
|
1302
|
+
const result = await postAction("/__auvrynt/dashboard/workspace", { path: selected.path });
|
|
1303
|
+
state.allowedRoots = result.allowedRoots || [selected.path];
|
|
1304
|
+
renderConnection();
|
|
1305
|
+
showToast(result.message || "Workspace changed.");
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1308
|
+
} finally {
|
|
1309
|
+
editWorkspaceButton.disabled = Boolean(lifecycleMode);
|
|
1310
|
+
editWorkspaceButton.setAttribute("aria-busy", "false");
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
sessionLimitForm.addEventListener("submit", async (event) => {
|
|
1315
|
+
event.preventDefault();
|
|
1316
|
+
const maxSessions = Number(sessionLimitInput.value);
|
|
1317
|
+
if (!Number.isInteger(maxSessions) || maxSessions < 1 || maxSessions > 999) {
|
|
1318
|
+
showToast("MCP session limit must be between 1 and 999.", "error");
|
|
1319
|
+
sessionLimitInput.focus();
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
if (maxSessions === state.maxSessions) {
|
|
1323
|
+
showToast("MCP session limit is already " + maxSessions + ".");
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
saveSessionLimitButton.disabled = true;
|
|
1327
|
+
saveSessionLimitButton.setAttribute("aria-busy", "true");
|
|
1328
|
+
sessionLimitInput.disabled = true;
|
|
1329
|
+
try {
|
|
1330
|
+
const result = await postAction("/__auvrynt/dashboard/session-limit", { maxSessions });
|
|
1331
|
+
state.maxSessions = result.maxSessions;
|
|
1332
|
+
renderAgentPresence();
|
|
1333
|
+
renderAnalytics();
|
|
1334
|
+
showToast(result.message || "MCP session limit changed.");
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1337
|
+
} finally {
|
|
1338
|
+
saveSessionLimitButton.disabled = false;
|
|
1339
|
+
saveSessionLimitButton.setAttribute("aria-busy", "false");
|
|
1340
|
+
sessionLimitInput.disabled = false;
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1344
|
+
sessionIdleTimeoutForm.addEventListener("submit", async (event) => {
|
|
1345
|
+
event.preventDefault();
|
|
1346
|
+
const idleTimeoutMinutes = Number(sessionIdleTimeoutInput.value);
|
|
1347
|
+
if (!Number.isInteger(idleTimeoutMinutes) || idleTimeoutMinutes < 1 || idleTimeoutMinutes > 10080) {
|
|
1348
|
+
showToast("MCP idle timeout must be between 1 minute and 10080 minutes (7 days).", "error");
|
|
1349
|
+
sessionIdleTimeoutInput.focus();
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (idleTimeoutMinutes === state.sessionIdleTimeoutMinutes) {
|
|
1353
|
+
showToast("MCP idle timeout is already " + idleTimeoutMinutes + " minutes.");
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
saveSessionIdleTimeoutButton.disabled = true;
|
|
1357
|
+
saveSessionIdleTimeoutButton.setAttribute("aria-busy", "true");
|
|
1358
|
+
sessionIdleTimeoutInput.disabled = true;
|
|
1359
|
+
try {
|
|
1360
|
+
const result = await postAction("/__auvrynt/dashboard/session-idle-timeout", { idleTimeoutMinutes });
|
|
1361
|
+
state.sessionIdleTimeoutMinutes = result.idleTimeoutMinutes;
|
|
1362
|
+
renderAgentPresence();
|
|
1363
|
+
showToast(result.message || "MCP idle timeout changed.");
|
|
1364
|
+
} catch (error) {
|
|
1365
|
+
showToast(error instanceof Error ? error.message : String(error), "error");
|
|
1366
|
+
} finally {
|
|
1367
|
+
saveSessionIdleTimeoutButton.disabled = false;
|
|
1368
|
+
saveSessionIdleTimeoutButton.setAttribute("aria-busy", "false");
|
|
1369
|
+
sessionIdleTimeoutInput.disabled = false;
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
async function copyText(text, button, successMessage) {
|
|
1374
|
+
try {
|
|
1375
|
+
await navigator.clipboard.writeText(text);
|
|
1376
|
+
button.textContent = "✓";
|
|
1377
|
+
button.classList.add("copied");
|
|
1378
|
+
showToast(successMessage);
|
|
1379
|
+
} catch (error) {
|
|
1380
|
+
showToast("Failed to copy: " + error.message, "error");
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
document.querySelectorAll("[data-copy-url]").forEach((button) => button.addEventListener("click", () => {
|
|
1385
|
+
void copyText(button.dataset.copyUrl === "local" ? state.localMcpUrl : state.publicMcpUrl, button, "URL copied.");
|
|
1386
|
+
}));
|
|
1387
|
+
|
|
1388
|
+
document.getElementById("command-search").addEventListener("input", (event) => {
|
|
1389
|
+
const query = event.target.value.trim().toLowerCase();
|
|
1390
|
+
document.querySelectorAll("[data-command-search]").forEach((row) => { row.hidden = Boolean(query && !row.dataset.commandSearch.includes(query)); });
|
|
1391
|
+
document.querySelectorAll("[data-command-group]").forEach((group) => { group.hidden = !group.querySelector("[data-command-search]:not([hidden])"); });
|
|
1392
|
+
});
|
|
1393
|
+
document.querySelectorAll("[data-log-filter]").forEach((button) => button.addEventListener("click", () => {
|
|
1394
|
+
activeFilter = button.dataset.logFilter;
|
|
1395
|
+
document.querySelectorAll("[data-log-filter]").forEach((item) => item.classList.toggle("active", item === button));
|
|
1396
|
+
renderLogs();
|
|
1397
|
+
}));
|
|
1398
|
+
logSearch.addEventListener("input", renderLogs);
|
|
1399
|
+
logs.addEventListener("scroll", () => { if (logs.scrollTop <= 4) unseenLogs = 0; updateNewLogButton(); });
|
|
1400
|
+
newLogs.addEventListener("click", () => { logs.scrollTop = 0; unseenLogs = 0; updateNewLogButton(); });
|
|
1401
|
+
|
|
1402
|
+
setActiveView(activeView, false);
|
|
1403
|
+
renderState(state);
|
|
1404
|
+
refreshTimer = window.setInterval(refresh, 2000);
|
|
1405
|
+
})();
|
|
1406
|
+
</script>
|
|
1407
|
+
</body>
|
|
1408
|
+
</html>`;
|
|
1409
|
+
}
|
|
1410
|
+
function commandMarkup() {
|
|
1411
|
+
const groups = groupCommands();
|
|
1412
|
+
const columns = [[], []];
|
|
1413
|
+
const weights = [0, 0];
|
|
1414
|
+
for (const entry of groups.entries()) {
|
|
1415
|
+
const index = weights[0] <= weights[1] ? 0 : 1;
|
|
1416
|
+
columns[index].push(entry);
|
|
1417
|
+
weights[index] += entry[1].length + 1;
|
|
1418
|
+
}
|
|
1419
|
+
return columns.map((column) => `<div class="command-column">${column.map(([group, items]) => `<section class="command-group" data-command-group><h3>${escapeHtml(group)}</h3>${items.map(commandRowMarkup).join("")}</section>`).join("")}</div>`).join("");
|
|
1420
|
+
}
|
|
1421
|
+
function commandRowMarkup(item) {
|
|
1422
|
+
return `<div class="command-row" data-command-search="${escapeHtml(`${item.command} ${item.description}`.toLowerCase())}"><code>${escapeHtml(item.command)}</code><span>${escapeHtml(item.description)}</span></div>`;
|
|
1423
|
+
}
|
|
1424
|
+
function groupCommands() {
|
|
1425
|
+
const groups = new Map();
|
|
1426
|
+
for (const item of AUVRYNT_COMMANDS) {
|
|
1427
|
+
const command = item.command.toLowerCase();
|
|
1428
|
+
const group = command.includes("token") || command.includes("connected")
|
|
1429
|
+
? "Authentication"
|
|
1430
|
+
: command.includes("config") || command.includes("init") || command.includes("setup")
|
|
1431
|
+
? "Configuration"
|
|
1432
|
+
: command.includes("doctor") || command.includes("status")
|
|
1433
|
+
? "Diagnostics"
|
|
1434
|
+
: command.includes("enable") || command.includes("disable") || command.includes(" add ")
|
|
1435
|
+
? "Integrations"
|
|
1436
|
+
: command.includes("uninstall")
|
|
1437
|
+
? "Cleanup"
|
|
1438
|
+
: "Server lifecycle";
|
|
1439
|
+
const items = groups.get(group) ?? [];
|
|
1440
|
+
items.push(item);
|
|
1441
|
+
groups.set(group, items);
|
|
1442
|
+
}
|
|
1443
|
+
return groups;
|
|
1444
|
+
}
|
|
1445
|
+
function agentStateLabel(view) {
|
|
1446
|
+
const provider = view.agentProvider ?? "AI agent";
|
|
1447
|
+
if (view.agentState === "working")
|
|
1448
|
+
return `${provider} working`;
|
|
1449
|
+
if (view.agentState === "connected")
|
|
1450
|
+
return `${provider} connected`;
|
|
1451
|
+
if (view.agentState === "stopping")
|
|
1452
|
+
return "Server stopping";
|
|
1453
|
+
return "Waiting for AI";
|
|
1454
|
+
}
|
|
1455
|
+
function agentStateDetail(view) {
|
|
1456
|
+
if (view.agentState === "working")
|
|
1457
|
+
return `${view.activeToolCalls} active tool call${view.activeToolCalls === 1 ? "" : "s"}.`;
|
|
1458
|
+
if (view.agentState === "connected")
|
|
1459
|
+
return `${view.sessions} authenticated MCP session${view.sessions === 1 ? "" : "s"}.`;
|
|
1460
|
+
if (view.agentState === "stopping")
|
|
1461
|
+
return "Closing local services and sessions.";
|
|
1462
|
+
return "No authenticated MCP session is connected.";
|
|
1463
|
+
}
|
|
1464
|
+
function agentHeadline(view) {
|
|
1465
|
+
const provider = view.agentProvider ?? "An AI client";
|
|
1466
|
+
if (view.agentState === "working")
|
|
1467
|
+
return `${provider} is actively using Auvrynt.`;
|
|
1468
|
+
if (view.agentState === "connected")
|
|
1469
|
+
return `${provider} is connected and ready.`;
|
|
1470
|
+
if (view.agentState === "stopping")
|
|
1471
|
+
return "Auvrynt is closing active connections.";
|
|
1472
|
+
return "Auvrynt is online and ready.";
|
|
1473
|
+
}
|
|
1474
|
+
function agentCaption(view) {
|
|
1475
|
+
if (view.agentState === "working")
|
|
1476
|
+
return `${view.activeToolCalls} tool call${view.activeToolCalls === 1 ? " is" : "s are"} executing now.`;
|
|
1477
|
+
if (view.agentState === "connected")
|
|
1478
|
+
return `${view.sessions} authenticated MCP session${view.sessions === 1 ? " is" : "s are"} currently open.`;
|
|
1479
|
+
if (view.agentState === "stopping")
|
|
1480
|
+
return "The dashboard will stop refreshing when shutdown completes.";
|
|
1481
|
+
return "Connect ChatGPT, Claude, or another MCP-compatible AI client.";
|
|
1482
|
+
}
|
|
1483
|
+
function lastSeenLabel(value) {
|
|
1484
|
+
if (!value)
|
|
1485
|
+
return "No agent activity yet";
|
|
1486
|
+
return "Active just now";
|
|
1487
|
+
}
|
|
1488
|
+
function agentChangeLabel(workspaceRoot, value) {
|
|
1489
|
+
return workspaceRoot && value ? "Auvrynt · updated now" : "No agent changes yet";
|
|
1490
|
+
}
|
|
1491
|
+
function formatMetricNumber(value) {
|
|
1492
|
+
const normalized = Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
1493
|
+
if (normalized < 100_000)
|
|
1494
|
+
return normalized.toLocaleString();
|
|
1495
|
+
return new Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 }).format(normalized);
|
|
1496
|
+
}
|
|
1497
|
+
function formatUptime(seconds) {
|
|
1498
|
+
const totalSeconds = Math.max(0, Math.floor(seconds));
|
|
1499
|
+
const days = Math.floor(totalSeconds / 86_400);
|
|
1500
|
+
const hours = Math.floor((totalSeconds % 86_400) / 3_600);
|
|
1501
|
+
const minutes = Math.floor((totalSeconds % 3_600) / 60);
|
|
1502
|
+
return [days > 0 ? `${days}d` : "", hours > 0 ? `${hours}h` : "", `${minutes}m`].filter(Boolean).join(" ");
|
|
1503
|
+
}
|
|
1504
|
+
function jsonForHtml(value) {
|
|
1505
|
+
return JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll(">", "\\u003e").replaceAll("&", "\\u0026");
|
|
1506
|
+
}
|
|
1507
|
+
function escapeHtml(value) {
|
|
1508
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1509
|
+
}
|