bohr-agent-sdk 0.1.100__py3-none-any.whl → 0.1.102__py3-none-any.whl

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.
Files changed (49) hide show
  1. {bohr_agent_sdk-0.1.100.dist-info → bohr_agent_sdk-0.1.102.dist-info}/METADATA +6 -2
  2. bohr_agent_sdk-0.1.102.dist-info/RECORD +80 -0
  3. dp/agent/adapter/adk/client/calculation_mcp_tool.py +10 -2
  4. dp/agent/cli/cli.py +126 -25
  5. dp/agent/cli/templates/__init__.py +1 -0
  6. dp/agent/cli/templates/calculation/simple.py.template +15 -0
  7. dp/agent/cli/templates/device/tescan_device.py.template +158 -0
  8. dp/agent/cli/templates/main.py.template +67 -0
  9. dp/agent/cli/templates/ui/__init__.py +1 -0
  10. dp/agent/cli/templates/ui/api/__init__.py +1 -0
  11. dp/agent/cli/templates/ui/api/config.py +32 -0
  12. dp/agent/cli/templates/ui/api/constants.py +61 -0
  13. dp/agent/cli/templates/ui/api/debug.py +257 -0
  14. dp/agent/cli/templates/ui/api/files.py +469 -0
  15. dp/agent/cli/templates/ui/api/files_upload.py +115 -0
  16. dp/agent/cli/templates/ui/api/files_user.py +50 -0
  17. dp/agent/cli/templates/ui/api/messages.py +161 -0
  18. dp/agent/cli/templates/ui/api/projects.py +146 -0
  19. dp/agent/cli/templates/ui/api/sessions.py +93 -0
  20. dp/agent/cli/templates/ui/api/utils.py +161 -0
  21. dp/agent/cli/templates/ui/api/websocket.py +184 -0
  22. dp/agent/cli/templates/ui/config/__init__.py +1 -0
  23. dp/agent/cli/templates/ui/config/agent_config.py +257 -0
  24. dp/agent/cli/templates/ui/frontend/index.html +13 -0
  25. dp/agent/cli/templates/ui/frontend/package.json +46 -0
  26. dp/agent/cli/templates/ui/frontend/tsconfig.json +26 -0
  27. dp/agent/cli/templates/ui/frontend/tsconfig.node.json +10 -0
  28. dp/agent/cli/templates/ui/frontend/ui-static/assets/index-DdAmKhul.js +105 -0
  29. dp/agent/cli/templates/ui/frontend/ui-static/assets/index-DfN2raU9.css +1 -0
  30. dp/agent/cli/templates/ui/frontend/ui-static/index.html +14 -0
  31. dp/agent/cli/templates/ui/frontend/vite.config.ts +37 -0
  32. dp/agent/cli/templates/ui/scripts/build_ui.py +56 -0
  33. dp/agent/cli/templates/ui/server/__init__.py +0 -0
  34. dp/agent/cli/templates/ui/server/app.py +98 -0
  35. dp/agent/cli/templates/ui/server/connection.py +210 -0
  36. dp/agent/cli/templates/ui/server/file_watcher.py +85 -0
  37. dp/agent/cli/templates/ui/server/middleware.py +43 -0
  38. dp/agent/cli/templates/ui/server/models.py +53 -0
  39. dp/agent/cli/templates/ui/server/session_manager.py +1158 -0
  40. dp/agent/cli/templates/ui/server/user_files.py +85 -0
  41. dp/agent/cli/templates/ui/server/utils.py +50 -0
  42. dp/agent/cli/templates/ui/test_download.py +98 -0
  43. dp/agent/cli/templates/ui/ui_utils.py +260 -0
  44. dp/agent/cli/templates/ui/websocket-server.py +87 -0
  45. dp/agent/server/storage/http_storage.py +1 -1
  46. bohr_agent_sdk-0.1.100.dist-info/RECORD +0 -40
  47. {bohr_agent_sdk-0.1.100.dist-info → bohr_agent_sdk-0.1.102.dist-info}/WHEEL +0 -0
  48. {bohr_agent_sdk-0.1.100.dist-info → bohr_agent_sdk-0.1.102.dist-info}/entry_points.txt +0 -0
  49. {bohr_agent_sdk-0.1.100.dist-info → bohr_agent_sdk-0.1.102.dist-info}/top_level.txt +0 -0
@@ -0,0 +1 @@
1
+ .btn-animated{position:relative;overflow:hidden;transition:all .3s cubic-bezier(.22,1,.36,1)}.btn-animated:before{content:"";position:absolute;top:50%;left:50%;width:0;height:0;background:radial-gradient(circle,rgba(255,255,255,.3) 0%,transparent 70%);transform:translate(-50%,-50%);transition:width .6s,height .6s;border-radius:50%}.btn-animated:hover:before{width:300px;height:300px}.btn-animated:hover{transform:translateY(-2px);box-shadow:0 10px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.btn-animated:active{transform:translateY(0);transition:transform .1s}.input-animated{position:relative;transition:all .3s cubic-bezier(.22,1,.36,1)}.input-animated:focus{transform:translateY(-1px);box-shadow:0 0 0 2px #3b82f633,0 4px 6px -1px #0000001a}.input-animated:after{content:"";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(90deg,#3b82f6,#8b5cf6);transform:scaleX(0);transform-origin:center;transition:transform .3s cubic-bezier(.22,1,.36,1)}.input-animated:focus:after{transform:scaleX(1)}.ripple{position:relative;overflow:hidden}.ripple-effect{position:absolute;border-radius:50%;background:#ffffff80;transform:scale(0);animation:ripple .6s ease-out;pointer-events:none}@keyframes ripple{to{transform:scale(4);opacity:0}}.bounce-in{animation:bounceIn .6s cubic-bezier(.68,-.55,.265,1.55)}@keyframes bounceIn{0%{opacity:0;transform:scale(.3)}50%{transform:scale(1.05)}70%{transform:scale(.95)}to{opacity:1;transform:scale(1)}}.pulse-soft{animation:pulseSoft 2s infinite}@keyframes pulseSoft{0%,to{transform:scale(1);opacity:1}50%{transform:scale(1.05);opacity:.8}}.glow{position:relative}.glow:before{content:"";position:absolute;top:-2px;left:-2px;right:-2px;bottom:-2px;background:linear-gradient(45deg,#3b82f6,#8b5cf6,#ec4899,#3b82f6);background-size:400% 400%;border-radius:inherit;opacity:0;transition:opacity .3s;filter:blur(8px);z-index:-1;animation:glowAnimation 3s ease infinite}.glow:hover:before,.glow:focus-within:before{opacity:.6}@keyframes glowAnimation{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}.underline-slide{position:relative;text-decoration:none}.underline-slide:after{content:"";position:absolute;bottom:0;left:0;width:100%;height:2px;background:linear-gradient(90deg,#3b82f6,#8b5cf6);transform:scaleX(0);transform-origin:right;transition:transform .3s cubic-bezier(.22,1,.36,1)}.underline-slide:hover:after{transform:scaleX(1);transform-origin:left}.flip-3d{transform-style:preserve-3d;transition:transform .6s cubic-bezier(.22,1,.36,1)}.flip-3d:hover{transform:rotateY(5deg) rotateX(-5deg)}.magnetic{transition:transform .3s cubic-bezier(.22,1,.36,1)}.liquid-button{position:relative;overflow:hidden;transition:all .4s cubic-bezier(.22,1,.36,1)}.liquid-button:before,.liquid-button:after{content:"";position:absolute;top:50%;left:50%;width:100%;height:100%;background:#ffffff1a;border-radius:50%;transform:translate(-50%,-50%) scale(0);transition:transform .5s ease-out}.liquid-button:hover:before{transform:translate(-50%,-50%) scale(1.5)}.liquid-button:hover:after{transform:translate(-50%,-50%) scale(1.2);transition-delay:.1s}.breathing{animation:breathing 3s ease-in-out infinite}@keyframes breathing{0%,to{opacity:1}50%{opacity:.6}}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}body:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Söhne,"ui-sans-serif","system-ui",-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}h1,h2,h3,h4,h5,h6{font-weight:500;letter-spacing:-.025em}::-moz-selection{background-color:#3b82f633;--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}::selection{background-color:#3b82f633;--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}:is(.dark *)::-moz-selection{background-color:#60a5fa33;--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}:is(.dark *)::selection{background-color:#60a5fa33;--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}html{scroll-behavior:smooth}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background-color:transparent}::-webkit-scrollbar-thumb{border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb{-webkit-transition:background-color .3s;transition:background-color .3s}::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}:is(.dark *)::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-gray{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151}.btn{display:flex;align-items:center;gap:.5rem;border-radius:.5rem;padding:.5rem 1rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn-primary{--tw-bg-opacity: 1;background-color:rgb(0 113 227 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-primary:hover{background-color:#0071e3e6}.btn-primary:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.btn-secondary{--tw-bg-opacity: 1;background-color:rgb(245 245 247 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(54 54 57 / var(--tw-text-opacity, 1))}.btn-secondary:hover{--tw-bg-opacity: 1;background-color:rgb(232 232 237 / var(--tw-bg-opacity, 1))}.btn-secondary:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.btn-ghost{--tw-text-opacity: 1;color:rgb(72 72 74 / var(--tw-text-opacity, 1))}.btn-ghost:hover{--tw-bg-opacity: 1;background-color:rgb(245 245 247 / var(--tw-bg-opacity, 1))}.btn-ghost:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.card{border-radius:1rem;border-width:1px;border-color:#e8e8ed80;--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));padding:1.5rem;--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}.card:hover{--tw-translate-y: -.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.\!input{width:100%;border-radius:.5rem;border-width:1px;border-color:transparent;--tw-bg-opacity: 1;background-color:rgb(245 245 247 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.\!input:focus{--tw-border-opacity: 1;border-color:rgb(0 113 227 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));outline:2px solid transparent;outline-offset:2px}.input{width:100%;border-radius:.5rem;border-width:1px;border-color:transparent;--tw-bg-opacity: 1;background-color:rgb(245 245 247 / var(--tw-bg-opacity, 1));padding:.75rem 1rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.input:focus{--tw-border-opacity: 1;border-color:rgb(0 113 227 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));outline:2px solid transparent;outline-offset:2px}.badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .625rem;font-size:.75rem;line-height:1rem;font-weight:500}.badge-success{background-color:#34c7591a;--tw-text-opacity: 1;color:rgb(52 199 89 / var(--tw-text-opacity, 1))}.badge-warning{background-color:#ffcc001a;--tw-text-opacity: 1;color:rgb(255 204 0 / var(--tw-text-opacity, 1))}.badge-error{background-color:#ff3b301a;--tw-text-opacity: 1;color:rgb(255 59 48 / var(--tw-text-opacity, 1))}.badge-info{background-color:#0071e31a;--tw-text-opacity: 1;color:rgb(0 113 227 / var(--tw-text-opacity, 1))}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.-bottom-2{bottom:-.5rem}.-left-2{left:-.5rem}.-right-2{right:-.5rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:.75rem}.right-0{right:0}.right-2{right:.5rem}.top-0{top:0}.top-1\/2{top:50%}.top-\[0\.6em\]{top:.6em}.z-10{z-index:10}.z-50{z-index:50}.order-1{order:1}.order-2{order:2}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-0\.5{margin-left:.125rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.\!inline{display:inline!important}.inline{display:inline}.flex{display:flex}.table{display:table}.hidden{display:none}.h-0\.5{height:.125rem}.h-1\.5{height:.375rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-96{max-height:24rem}.max-h-full{max-height:100%}.w-0\.5{width:.125rem}.w-1\.5{width:.375rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-8{width:2rem}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[3rem\]{min-width:3rem}.min-w-\[60px\]{min-width:60px}.min-w-full{min-width:100%}.max-w-4xl{max-width:56rem}.max-w-\[80\%\]{max-width:80%}.max-w-full{max-width:100%}.max-w-none{max-width:none}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-col-resize{cursor:col-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity, 1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-none{border-style:none}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-current{border-color:currentColor}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-200\/50{border-color:#e5e7eb80}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.border-yellow-200{--tw-border-opacity: 1;border-color:rgb(254 240 138 / var(--tw-border-opacity, 1))}.border-t-transparent{border-top-color:transparent}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/50{background-color:#ffffff80}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity, 1))}.bg-yellow-300{--tw-bg-opacity: 1;background-color:rgb(253 224 71 / var(--tw-bg-opacity, 1))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-blue-600{--tw-gradient-from: #2563eb var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-gray-50{--tw-gradient-from: #f9fafb var(--tw-gradient-from-position);--tw-gradient-to: rgb(249 250 251 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-gray-600{--tw-gradient-from: #4b5563 var(--tw-gradient-from-position);--tw-gradient-to: rgb(75 85 99 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-white{--tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #fff var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-blue-600{--tw-gradient-to: #2563eb var(--tw-gradient-to-position)}.to-gray-50{--tw-gradient-to: #f9fafb var(--tw-gradient-to-position)}.to-gray-700{--tw-gradient-to: #374151 var(--tw-gradient-to-position)}.to-purple-500{--tw-gradient-to: #a855f7 var(--tw-gradient-to-position)}.to-purple-600{--tw-gradient-to: #9333ea var(--tw-gradient-to-position)}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.object-contain{-o-object-fit:contain;object-fit:contain}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pl-2{padding-left:.5rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pl-9{padding-left:2.25rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[15px\]{font-size:15px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.tracking-wider{letter-spacing:.05em}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-cyan-500{--tw-text-opacity: 1;color:rgb(6 182 212 / var(--tw-text-opacity, 1))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-orange-500{--tw-text-opacity: 1;color:rgb(249 115 22 / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-pink-500{--tw-text-opacity: 1;color:rgb(236 72 153 / var(--tw-text-opacity, 1))}.text-purple-500{--tw-text-opacity: 1;color:rgb(168 85 247 / var(--tw-text-opacity, 1))}.text-purple-600{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.text-purple-700{--tw-text-opacity: 1;color:rgb(126 34 206 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity, 1))}.text-yellow-800{--tw-text-opacity: 1;color:rgb(133 77 14 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-blue-500\/20{--tw-ring-color: rgb(59 130 246 / .2)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.claude-markdown{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.claude-markdown:is(.dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.claude-markdown{font-size:15px;line-height:1.75;letter-spacing:-.004em}.claude-markdown>*:first-child{margin-top:0!important}.claude-markdown>*:last-child{margin-bottom:0!important}.claude-markdown ul{list-style:none;padding-left:0}.claude-markdown ol{counter-reset:list-counter;list-style:none;padding-left:0}.claude-markdown ol>li{counter-increment:list-counter;position:relative;padding-left:2rem}.claude-markdown ol>li:before{content:counter(list-counter) ".";position:absolute;left:0;top:0;font-weight:500;--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.claude-markdown ol>li:is(.dark *):before{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.claude-markdown ol>li>span:first-child{display:none}.claude-markdown code{font-size:.875rem;line-height:1.25rem}.claude-markdown a{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.claude-markdown a:hover{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.claude-markdown a:is(.dark *){--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.claude-markdown a:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.claude-markdown a{text-decoration:none;border-bottom:1px solid currentColor;transition:all .2s ease}.claude-markdown a:hover{border-bottom-color:transparent}.claude-markdown table{margin-top:1rem;margin-bottom:1rem;width:100%;border-collapse:collapse}.claude-markdown th{border-bottom-width:2px;--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1));padding:.75rem;text-align:left;font-weight:600}.claude-markdown th:is(.dark *){--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.claude-markdown td{border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1));padding:.75rem}.claude-markdown td:is(.dark *){--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.claude-markdown tr:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.claude-markdown tr:hover:is(.dark *){background-color:#1f293780}.terminal{font-family:Monaco,Consolas,Courier New,monospace}.terminal-container{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.terminal-header{border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.terminal-output{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1));font-family:Monaco,Consolas,Courier New,monospace;font-size:13px;line-height:1.5}.terminal-input{background-color:transparent;--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1));font-family:Monaco,Consolas,Courier New,monospace;font-size:13px}.terminal-command{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.terminal-error{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.terminal-output pre{white-space:pre-wrap;overflow-wrap:break-word}.terminal-output::-webkit-scrollbar{width:8px}.terminal-output::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.terminal-output::-webkit-scrollbar-thumb{border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.terminal-output::-webkit-scrollbar-thumb:hover{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.resize-handle{position:relative;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.resize-handle:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0}.resize-handle-horizontal{cursor:col-resize}.resize-handle-horizontal:before{left:-.25rem;right:-.25rem}.resize-handle-vertical{cursor:row-resize}.resize-handle-vertical:before{top:-.25rem;bottom:-.25rem}.resize-handle:hover,.resize-handle.resizing{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}body.resizing-horizontal{cursor:col-resize!important}body.resizing-vertical{cursor:row-resize!important}body.resizing *{cursor:inherit!important}body.no-select{user-select:none!important;-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important}.resizable-transition{transition:width .1s ease-out,height .1s ease-out}.resize-area-active{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));background-color:#3b82f61a}.file-tree-resize-handle{position:absolute;top:0;right:0;height:100%;width:.25rem;cursor:col-resize;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;background:transparent}.file-tree-resize-handle:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.file-tree-resize-handle:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;left:-.25rem;right:-.25rem}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{transform:translateY(10px);opacity:0}to{transform:translateY(0);opacity:1}}.glass-premium{background:#fff9;backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border:1px solid rgba(255,255,255,.3);box-shadow:0 8px 32px #1f268726,inset 0 0 0 1px #ffffff1a}.dark .glass-premium{background:#11182799;border:1px solid rgba(255,255,255,.1);box-shadow:0 8px 32px #0000004d,inset 0 0 0 1px #ffffff0d}.glass-glossy{position:relative;overflow:hidden}.glass-glossy:before{content:"";position:absolute;top:-50%;left:-50%;width:200%;height:200%;background:linear-gradient(45deg,transparent 30%,rgba(255,255,255,.1) 50%,transparent 70%);transform:rotate(45deg);transition:all .5s;pointer-events:none}.glass-glossy:hover:before{animation:glossy-shine .5s ease-out}@keyframes glossy-shine{0%{transform:translate(-100%) translateY(-100%) rotate(45deg)}to{transform:translate(100%) translateY(100%) rotate(45deg)}}.shadow-depth{box-shadow:0 1px 2px #00000012,0 2px 4px #00000012,0 4px 8px #00000012,0 8px 16px #00000012,0 16px 32px #00000012,0 32px 64px #00000012}.aurora-bg{position:relative;overflow:hidden}.aurora-bg:before{content:"";position:absolute;top:-50%;left:-50%;width:200%;height:200%;background:conic-gradient(from 0deg at 50% 50%,#3b82f61a,#9333ea1a,#ec48991a 120deg,#3b82f61a 360deg);animation:aurora-rotate 20s linear infinite;filter:blur(40px);z-index:-1}@keyframes aurora-rotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.dark\:prose-invert:is(.dark *){--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.first\:mt-0:first-child{margin-top:0}.hover\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-300:hover{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-700:hover{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.hover\:bg-red-100:hover{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.hover\:from-blue-600:hover{--tw-gradient-from: #2563eb var(--tw-gradient-from-position);--tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:to-blue-700:hover{--tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position)}.hover\:text-blue-600:hover{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.hover\:text-blue-700:hover{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.hover\:text-blue-800:hover{--tw-text-opacity: 1;color:rgb(30 64 175 / var(--tw-text-opacity, 1))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:text-red-600:hover{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-lg:hover{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-xl:hover{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.group:hover .group-hover\:opacity-100{opacity:1}.dark\:divide-gray-700:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(55 65 81 / var(--tw-divide-opacity, 1))}.dark\:border-blue-400:is(.dark *){--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.dark\:border-blue-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(30 64 175 / var(--tw-border-opacity, 1))}.dark\:border-gray-600:is(.dark *){--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.dark\:border-gray-700:is(.dark *){--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.dark\:border-gray-700\/50:is(.dark *){border-color:#37415180}.dark\:border-green-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(22 101 52 / var(--tw-border-opacity, 1))}.dark\:border-red-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(153 27 27 / var(--tw-border-opacity, 1))}.dark\:border-yellow-800:is(.dark *){--tw-border-opacity: 1;border-color:rgb(133 77 14 / var(--tw-border-opacity, 1))}.dark\:bg-blue-900:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(30 58 138 / var(--tw-bg-opacity, 1))}.dark\:bg-blue-900\/20:is(.dark *){background-color:#1e3a8a33}.dark\:bg-blue-900\/30:is(.dark *){background-color:#1e3a8a4d}.dark\:bg-blue-950\/20:is(.dark *){background-color:#17255433}.dark\:bg-gray-500:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-600:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-700:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-700\/50:is(.dark *){background-color:#37415180}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-800\/50:is(.dark *){background-color:#1f293780}.dark\:bg-gray-900:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.dark\:bg-green-900\/20:is(.dark *){background-color:#14532d33}.dark\:bg-green-900\/30:is(.dark *){background-color:#14532d4d}.dark\:bg-red-900\/20:is(.dark *){background-color:#7f1d1d33}.dark\:bg-red-900\/30:is(.dark *){background-color:#7f1d1d4d}.dark\:bg-yellow-600:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity, 1))}.dark\:bg-yellow-900\/20:is(.dark *){background-color:#713f1233}.dark\:bg-yellow-900\/30:is(.dark *){background-color:#713f124d}.dark\:from-gray-900:is(.dark *){--tw-gradient-from: #111827 var(--tw-gradient-from-position);--tw-gradient-to: rgb(17 24 39 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.dark\:via-gray-800:is(.dark *){--tw-gradient-to: rgb(31 41 55 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #1f2937 var(--tw-gradient-via-position), var(--tw-gradient-to)}.dark\:to-gray-900:is(.dark *){--tw-gradient-to: #111827 var(--tw-gradient-to-position)}.dark\:text-blue-300:is(.dark *){--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.dark\:text-gray-100:is(.dark *){--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.dark\:text-gray-200:is(.dark *){--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.dark\:text-gray-500:is(.dark *){--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.dark\:text-gray-600:is(.dark *){--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.dark\:text-green-400:is(.dark *){--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.dark\:text-orange-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.dark\:text-purple-400:is(.dark *){--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.dark\:text-red-300:is(.dark *){--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.dark\:text-yellow-200:is(.dark *){--tw-text-opacity: 1;color:rgb(254 240 138 / var(--tw-text-opacity, 1))}.dark\:text-yellow-300:is(.dark *){--tw-text-opacity: 1;color:rgb(253 224 71 / var(--tw-text-opacity, 1))}.dark\:text-yellow-400:is(.dark *){--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.dark\:hover\:bg-gray-600:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-gray-700:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-gray-800:hover:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-red-900\/20:hover:is(.dark *){background-color:#7f1d1d33}.dark\:hover\:text-blue-300:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.dark\:hover\:text-gray-200:hover:is(.dark *){--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.dark\:focus\:ring-blue-400:focus:is(.dark *){--tw-ring-opacity: 1;--tw-ring-color: rgb(96 165 250 / var(--tw-ring-opacity, 1))}
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/nexus-icon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Agent UI</title>
8
+ <script type="module" crossorigin src="/assets/index-DdAmKhul.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DfN2raU9.css">
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,37 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ resolve: {
8
+ alias: {
9
+ '@': path.resolve(__dirname, './src'),
10
+ },
11
+ },
12
+ build: {
13
+ outDir: 'ui-static',
14
+ },
15
+ server: {
16
+ // Use environment variable or default to 3000 for dev server
17
+ port: parseInt(process.env.FRONTEND_PORT || '3000'),
18
+ host: '0.0.0.0',
19
+ allowedHosts: true,
20
+ proxy: {
21
+ // Proxy API and WebSocket requests to the backend server
22
+ '/api/materials_db': {
23
+ target: `https://www.test.bohrium.com`,
24
+ changeOrigin: true,
25
+ },
26
+ '/api': {
27
+ target: `http://localhost:${process.env.VITE_WS_PORT || '8000'}`,
28
+ changeOrigin: true,
29
+ },
30
+ '/ws': {
31
+ target: `ws://localhost:${process.env.VITE_WS_PORT || '8000'}`,
32
+ ws: true,
33
+ changeOrigin: true,
34
+ },
35
+ }
36
+ }
37
+ })
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 构建前端静态文件的脚本
4
+ 在打包发布前运行此脚本以生成静态文件
5
+ """
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+ def build_frontend():
11
+ """构建前端静态文件"""
12
+ # 获取脚本所在目录
13
+ script_dir = Path(__file__).parent
14
+ # frontend 在上一级目录
15
+ frontend_dir = script_dir.parent / "frontend"
16
+
17
+ if not frontend_dir.exists():
18
+ print(f"错误: 找不到前端目录 {frontend_dir}")
19
+ return False
20
+
21
+ print(f"开始构建前端...")
22
+ os.chdir(frontend_dir)
23
+
24
+ # 检查是否安装了依赖
25
+ if not (frontend_dir / "node_modules").exists():
26
+ print("安装前端依赖...")
27
+ result = subprocess.run(["npm", "install"], capture_output=True, text=True)
28
+ if result.returncode != 0:
29
+ print(f"安装依赖失败: {result.stderr}")
30
+ return False
31
+
32
+ # 构建前端
33
+ print("构建前端项目...")
34
+ result = subprocess.run(["npm", "run", "build"], capture_output=True, text=True)
35
+ if result.returncode != 0:
36
+ print(f"构建失败: {result.stderr}")
37
+ return False
38
+
39
+ # 检查构建输出
40
+ dist_dir = frontend_dir / "ui-static"
41
+ if not dist_dir.exists():
42
+ print("错误: 构建输出目录不存在")
43
+ return False
44
+
45
+ print(f"构建成功! 输出目录: {dist_dir}")
46
+
47
+ # 列出构建的文件
48
+ files = list(dist_dir.rglob("*"))
49
+ print(f"构建了 {len([f for f in files if f.is_file()])} 个文件")
50
+
51
+ return True
52
+
53
+ if __name__ == "__main__":
54
+ import sys
55
+ success = build_frontend()
56
+ sys.exit(0 if success else 1)
File without changes
@@ -0,0 +1,98 @@
1
+ """
2
+ FastAPI application creation and configuration
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+ from datetime import datetime
7
+ from fastapi import FastAPI
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from fastapi.staticfiles import StaticFiles
10
+
11
+ from server.middleware import RequestLoggingMiddleware, HostValidationMiddleware
12
+ from config.agent_config import agentconfig
13
+
14
+ # Import all API endpoints
15
+ from api import websocket, files, sessions, config as config_api, projects, debug
16
+ from api.files_upload import upload_files
17
+ from api.files_user import get_user_file
18
+
19
+
20
+ def create_app() -> FastAPI:
21
+ """Create and configure FastAPI application"""
22
+
23
+ # Create FastAPI instance
24
+ app = FastAPI(title="Agent WebSocket Server")
25
+
26
+ # Get server config
27
+ server_config = agentconfig.get_server_config()
28
+ allowed_hosts = server_config.get("allowedHosts", ["localhost", "127.0.0.1", "0.0.0.0"])
29
+
30
+ # Build allowed CORS origins
31
+ allowed_origins = []
32
+ for host in allowed_hosts:
33
+ allowed_origins.extend([
34
+ f"http://{host}:*",
35
+ f"https://{host}:*",
36
+ f"http://{host}",
37
+ f"https://{host}"
38
+ ])
39
+
40
+ # Add CORS middleware
41
+ app.add_middleware(
42
+ CORSMiddleware,
43
+ allow_origins=allowed_origins,
44
+ allow_credentials=True,
45
+ allow_methods=["*"],
46
+ allow_headers=["*"],
47
+ )
48
+
49
+ # Add custom middleware
50
+ # Note: middleware executes in reverse order, last added executes first
51
+ # So add HostValidation first, then RequestLogging
52
+ app.add_middleware(HostValidationMiddleware, allowed_hosts=allowed_hosts)
53
+ app.add_middleware(RequestLoggingMiddleware)
54
+
55
+ # Register routes
56
+ # WebSocket endpoint
57
+ app.add_websocket_route("/ws", websocket.websocket_endpoint)
58
+
59
+ # API routes
60
+ app.get("/api/status")(config_api.status)
61
+ app.get("/api/config")(config_api.get_config)
62
+ app.get("/api/files/tree")(files.get_file_tree)
63
+ app.get("/api/files/{user_id}/output/{filename}")(get_user_file)
64
+ app.get("/api/files{file_path:path}")(files.get_file_content)
65
+ app.get("/api/download/file{file_path:path}")(files.download_file)
66
+ app.get("/api/download/folder{folder_path:path}")(files.download_folder)
67
+ app.post("/api/upload")(upload_files)
68
+ app.delete("/api/files{file_path:path}")(files.delete_file)
69
+ app.delete("/api/sessions/clear")(sessions.clear_user_sessions)
70
+ app.get("/api/sessions/export")(sessions.export_user_sessions)
71
+ app.get("/api/projects")(projects.get_projects)
72
+
73
+ # Debug routes (only in development)
74
+ if os.environ.get('DEBUG', '').lower() in ['true', '1', 'yes']:
75
+ app.get("/api/debug/runners")(debug.get_runner_status)
76
+ app.get("/api/debug/config")(debug.get_config_status)
77
+ app.get("/api/debug/sessions")(debug.get_session_status)
78
+ app.get("/api/debug/test-agent")(debug.test_agent_creation)
79
+
80
+ # Mount static file service
81
+ # Get UI static file directory
82
+ ui_template_dir = Path(os.environ.get('UI_TEMPLATE_DIR', Path(__file__).parent.parent))
83
+ static_dir = ui_template_dir / "frontend" / "ui-static"
84
+
85
+ # Check if static file directory exists
86
+ if static_dir.exists():
87
+ # Define all other routes first, then mount static files last
88
+ # This ensures API and WebSocket routes are matched first
89
+ app.mount("/", StaticFiles(directory=str(static_dir), html=True), name="static")
90
+ print(f"📁 Static files directory: {static_dir}")
91
+ else:
92
+ print(f"⚠️ Static files directory does not exist: {static_dir}")
93
+
94
+ # Print Agent config info
95
+ print(f"📂 Agent configuration: {agentconfig.config['agent']['module']}")
96
+ print("🛠️ Agent will be dynamically created based on user AK when connecting")
97
+
98
+ return app
@@ -0,0 +1,210 @@
1
+ """
2
+ WebSocket connection management
3
+ """
4
+ import os
5
+ import uuid
6
+ import asyncio
7
+ from typing import Optional, List
8
+ from fastapi import WebSocket
9
+ from watchdog.observers import Observer
10
+ from bohrium_open_sdk import OpenSDK
11
+
12
+ from server.file_watcher import FileChangeHandler
13
+ from server.user_files import UserFileManager
14
+ from config.agent_config import agentconfig
15
+
16
+
17
+ class ConnectionContext:
18
+ """
19
+ WebSocket connection context
20
+ Manages independent state and resources for each connection
21
+ """
22
+
23
+ def __init__(self, websocket: WebSocket, access_key: str = "", app_key: str = ""):
24
+ """
25
+ Initialize connection context
26
+
27
+ Args:
28
+ websocket: WebSocket connection
29
+ access_key: Bohrium access key
30
+ app_key: Bohrium app key
31
+ """
32
+ # Basic connection info
33
+ self.websocket = websocket
34
+ self.access_key = access_key
35
+ self.app_key = app_key
36
+
37
+ # User identification
38
+ self.user_id = f"user_{uuid.uuid4().hex[:8]}" # ADK user ID
39
+ self.bohrium_user_id: Optional[str] = None # Bohrium user ID
40
+
41
+ # Project and session info
42
+ self.project_id: Optional[int] = None
43
+ self.current_session_id: Optional[str] = None
44
+
45
+ # File watchers
46
+ self.file_observers: List[Observer] = []
47
+
48
+ # Connection state
49
+ self.is_connected = True
50
+ self.connected_at = asyncio.get_event_loop().time()
51
+
52
+ async def init_bohrium_user_id(self):
53
+ """
54
+ Asynchronously initialize Bohrium user ID
55
+ Get user info through OpenSDK
56
+ """
57
+ if self.access_key and self.app_key:
58
+ try:
59
+ # Execute sync operation in thread pool
60
+ loop = asyncio.get_event_loop()
61
+ user_info = await loop.run_in_executor(
62
+ None,
63
+ self._get_bohrium_user_info
64
+ )
65
+
66
+ if user_info and user_info.get('code') == 0:
67
+ data = user_info.get('data', {})
68
+ self.bohrium_user_id = data.get('user_id')
69
+
70
+ except Exception:
71
+ pass
72
+
73
+ # Initialize file watchers
74
+ self._setup_file_watchers()
75
+
76
+ def _get_bohrium_user_info(self):
77
+ """
78
+ Sync method: Get Bohrium user info
79
+ """
80
+ client = OpenSDK(
81
+ access_key=self.access_key,
82
+ app_key=self.app_key
83
+ )
84
+ return client.user.get_info()
85
+
86
+ def get_user_identifier(self) -> str:
87
+ """
88
+ Get unique user identifier
89
+ Prefer bohrium_user_id, fallback to generated user_id
90
+
91
+ Returns:
92
+ User identifier
93
+ """
94
+ return self.bohrium_user_id if self.bohrium_user_id else self.user_id
95
+
96
+ def is_registered_user(self) -> bool:
97
+ """
98
+ Check if user is registered
99
+
100
+ Returns:
101
+ True if has bohrium_user_id
102
+ """
103
+ return bool(self.bohrium_user_id)
104
+
105
+ def set_project_id(self, project_id: int):
106
+ """
107
+ Set project ID
108
+
109
+ Args:
110
+ project_id: Project ID
111
+ """
112
+ self.project_id = project_id
113
+
114
+ def _setup_file_watchers(self):
115
+ """
116
+ Setup file watchers
117
+ Monitor changes in user file directories
118
+ """
119
+ try:
120
+ # Get user-specific file directory
121
+ user_working_dir = os.environ.get('USER_WORKING_DIR', os.getcwd())
122
+ files_config = agentconfig.get_files_config()
123
+ sessions_dir = files_config.get('sessionsDir', '.agent_sessions')
124
+
125
+ user_file_manager = UserFileManager(user_working_dir, sessions_dir)
126
+ user_files_dir = user_file_manager.get_user_files_dir(user_id=self.get_user_identifier())
127
+
128
+ watch_path = str(user_files_dir)
129
+
130
+ # Ensure directory exists
131
+ if not os.path.exists(watch_path):
132
+ os.makedirs(watch_path, exist_ok=True)
133
+
134
+ # Create file watcher
135
+ event_handler = FileChangeHandler(self, watch_path)
136
+ observer = Observer()
137
+ observer.schedule(event_handler, watch_path, recursive=True)
138
+ observer.start()
139
+
140
+ self.file_observers.append(observer)
141
+
142
+ except Exception:
143
+ pass
144
+
145
+ def cleanup(self):
146
+ """
147
+ Clean up connection resources
148
+ Stop file watchers etc.
149
+ """
150
+ # Mark connection as disconnected
151
+ self.is_connected = False
152
+
153
+ # Stop all file watchers
154
+ for observer in self.file_observers:
155
+ try:
156
+ observer.stop()
157
+ observer.join(timeout=1)
158
+ except Exception:
159
+ pass
160
+
161
+ self.file_observers.clear()
162
+
163
+ async def send_json(self, data: dict):
164
+ """
165
+ Send JSON data to client
166
+
167
+ Args:
168
+ data: Data to send
169
+ """
170
+ if self.is_connected:
171
+ try:
172
+ await self.websocket.send_json(data)
173
+ except Exception:
174
+ self.is_connected = False
175
+
176
+ async def receive_json(self) -> Optional[dict]:
177
+ """
178
+ Receive JSON data from client
179
+
180
+ Returns:
181
+ Received data or None
182
+ """
183
+ if self.is_connected:
184
+ try:
185
+ return await self.websocket.receive_json()
186
+ except Exception:
187
+ self.is_connected = False
188
+ return None
189
+ return None
190
+
191
+ def get_connection_info(self) -> dict:
192
+ """
193
+ Get connection info
194
+
195
+ Returns:
196
+ Connection info dict
197
+ """
198
+ current_time = asyncio.get_event_loop().time()
199
+ return {
200
+ "user_id": self.get_user_identifier(),
201
+ "is_registered": self.is_registered_user(),
202
+ "project_id": self.project_id,
203
+ "current_session_id": self.current_session_id,
204
+ "connected_duration": current_time - self.connected_at,
205
+ "is_connected": self.is_connected
206
+ }
207
+
208
+ def __repr__(self) -> str:
209
+ """String representation"""
210
+ return f"<ConnectionContext user={self.get_user_identifier()} connected={self.is_connected}>"
@@ -0,0 +1,85 @@
1
+ """
2
+ File monitoring functionality
3
+ """
4
+ import asyncio
5
+ import time
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING
8
+ from datetime import datetime
9
+ from watchdog.events import FileSystemEventHandler, FileSystemEvent
10
+
11
+ if TYPE_CHECKING:
12
+ from server.connection import ConnectionContext
13
+
14
+
15
+ class FileChangeHandler(FileSystemEventHandler):
16
+ """Handle file system change events"""
17
+ def __init__(self, context: 'ConnectionContext', watch_path: str):
18
+ self.context = context
19
+ self.watch_path = watch_path
20
+ self.last_event_time = {}
21
+ self.debounce_seconds = 0.5 # Debounce time
22
+
23
+ def should_ignore_path(self, path: str) -> bool:
24
+ """Check if this path should be ignored"""
25
+ # Ignore hidden files and temporary files
26
+ path_obj = Path(path)
27
+ for part in path_obj.parts:
28
+ if part.startswith('.') or part.endswith('~') or part.endswith('.tmp'):
29
+ return True
30
+ return False
31
+
32
+ def debounce_event(self, event_key: str) -> bool:
33
+ """Event debounce, avoid duplicate events"""
34
+ current_time = time.time()
35
+ last_time = self.last_event_time.get(event_key, 0)
36
+
37
+ if current_time - last_time < self.debounce_seconds:
38
+ return True # Should ignore this event
39
+
40
+ self.last_event_time[event_key] = current_time
41
+ return False
42
+
43
+ async def notify_file_change(self, event_type: str, path: str):
44
+ """Notify frontend of file changes"""
45
+ try:
46
+ # Calculate relative path
47
+ import os
48
+ rel_path = os.path.relpath(path, self.watch_path)
49
+
50
+ await self.context.websocket.send_json({
51
+ "type": "file_change",
52
+ "event_type": event_type,
53
+ "path": path,
54
+ "relative_path": rel_path,
55
+ "watch_directory": self.watch_path,
56
+ "timestamp": datetime.now().isoformat()
57
+ })
58
+ except Exception as e:
59
+ pass
60
+
61
+ def on_any_event(self, event: FileSystemEvent):
62
+ """Handle all file system events"""
63
+ if event.is_directory:
64
+ return # Temporarily ignore directory events
65
+
66
+ if self.should_ignore_path(event.src_path):
67
+ return
68
+
69
+ # Debounce handling
70
+ event_key = f"{event.event_type}:{event.src_path}"
71
+ if self.debounce_event(event_key):
72
+ return
73
+
74
+ # Map event types
75
+ event_map = {
76
+ 'created': 'created',
77
+ 'modified': 'modified',
78
+ 'deleted': 'deleted',
79
+ 'moved': 'moved'
80
+ }
81
+
82
+ event_type = event_map.get(event.event_type, event.event_type)
83
+
84
+ # Use asyncio to run async notification in event loop
85
+ asyncio.create_task(self.notify_file_change(event_type, event.src_path))