open-edison 0.1.64__tar.gz → 0.1.72rc1__tar.gz
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.
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/PKG-INFO +1 -1
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/development/contributing.md +1 -1
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/development/development_guide.md +1 -1
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/pyproject.toml +1 -1
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/cli.py +5 -4
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/config.py +31 -27
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/events.py +5 -2
- open_edison-0.1.72rc1/src/frontend_dist/assets/index-D05VN_1l.css +1 -0
- open_edison-0.1.72rc1/src/frontend_dist/assets/index-D6ziuTsl.js +51 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/frontend_dist/index.html +2 -2
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/frontend_dist/sw.js +22 -2
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/exporters.py +1 -1
- open_edison-0.1.72rc1/src/mcp_stdio_capture.py +144 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/middleware/data_access_tracker.py +49 -4
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/middleware/session_tracking.py +123 -34
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/oauth_manager.py +2 -2
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/permissions.py +86 -9
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/server.py +27 -6
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/setup_tui/main.py +6 -4
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/single_user_mcp.py +246 -109
- open_edison-0.1.64/src/frontend_dist/assets/index-BUUcUfTt.js +0 -51
- open_edison-0.1.64/src/frontend_dist/assets/index-o6_8mdM8.css +0 -1
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/.gitignore +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/LICENSE +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/README.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/config.json +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/desktop_ext/README.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/README.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/architecture/single_user_design.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/core/configuration.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/core/project_structure.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/core/proxy_usage.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/deployment/docker.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/deployment/local.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/development/testing.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/quick-reference/api_reference.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/quick-reference/config_quick_start.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/docs/testing_status.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/hatch_build.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/installation_test/README.md +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/prompt_permissions.json +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/resource_permissions.json +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/__init__.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/__main__.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/config.pyi +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/__init__.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/__main__.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/api.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/cli.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/export_cli.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/import_api.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/importers.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/merge.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/parsers.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/paths.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/quick_cli.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/mcp_importer/types.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/oauth_override.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/setup_tui/__init__.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/telemetry.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/tools/io.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/src/vulture_whitelist.py +0 -0
- {open_edison-0.1.64 → open_edison-0.1.72rc1}/tool_permissions.json +0 -0
@@ -360,7 +360,7 @@ from pathlib import Path
|
|
360
360
|
from fastapi import FastAPI
|
361
361
|
from loguru import logger as log
|
362
362
|
|
363
|
-
#
|
363
|
+
# Project imports (top-level; avoid local-in-function imports)
|
364
364
|
from src.config import Config
|
365
365
|
from src.single_user_mcp import SingleUserMCP
|
366
366
|
```
|
@@ -104,10 +104,6 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
104
104
|
|
105
105
|
|
106
106
|
async def _run_server(args: Any) -> None:
|
107
|
-
# Resolve config dir and expose via env for the rest of the app
|
108
|
-
config_dir_arg = getattr(args, "config_dir", None)
|
109
|
-
if config_dir_arg is not None:
|
110
|
-
os.environ["OPEN_EDISON_CONFIG_DIR"] = str(Path(config_dir_arg).expanduser().resolve())
|
111
107
|
config_dir = get_config_dir()
|
112
108
|
|
113
109
|
# Load config after setting env override
|
@@ -128,6 +124,11 @@ async def _run_server(args: Any) -> None:
|
|
128
124
|
def main(argv: list[str] | None = None) -> NoReturn: # noqa: C901
|
129
125
|
args = _parse_args(argv)
|
130
126
|
|
127
|
+
# Resolve config dir and expose via env for the rest of the app
|
128
|
+
config_dir_arg = getattr(args, "config_dir", None)
|
129
|
+
if config_dir_arg is not None:
|
130
|
+
os.environ["OPEN_EDISON_CONFIG_DIR"] = str(Path(config_dir_arg).expanduser().resolve())
|
131
|
+
|
131
132
|
if args.command is None:
|
132
133
|
args.command = "run"
|
133
134
|
|
@@ -108,21 +108,23 @@ class MCPServerConfig:
|
|
108
108
|
Remote servers use mcp-remote with HTTPS URLs and may require OAuth.
|
109
109
|
Local servers run as child processes and don't need OAuth.
|
110
110
|
"""
|
111
|
-
if
|
112
|
-
|
113
|
-
|
114
|
-
#
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
111
|
+
# TODO find out if having the remote_server functionality is necessary, and if so how we can make it interact well with servers who don't like the fastmcp networking stack.
|
112
|
+
return False
|
113
|
+
# if self.command != "npx":
|
114
|
+
# return False
|
115
|
+
|
116
|
+
# # Be tolerant of npx flags by scanning for 'mcp-remote' and the subsequent HTTPS URL
|
117
|
+
# try:
|
118
|
+
# if "mcp-remote" not in self.args:
|
119
|
+
# return False
|
120
|
+
# idx: int = self.args.index("mcp-remote")
|
121
|
+
# # Look for first https?:// argument after 'mcp-remote'
|
122
|
+
# for candidate in self.args[idx + 1 :]:
|
123
|
+
# if candidate.startswith(("https://", "http://")):
|
124
|
+
# return candidate.startswith("https://")
|
125
|
+
# return False
|
126
|
+
# except Exception:
|
127
|
+
# return False
|
126
128
|
|
127
129
|
def get_remote_url(self) -> str | None:
|
128
130
|
"""
|
@@ -131,17 +133,19 @@ class MCPServerConfig:
|
|
131
133
|
Returns:
|
132
134
|
The HTTPS URL if this is a remote server, None otherwise
|
133
135
|
"""
|
134
|
-
#
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
136
|
+
# TODO see above
|
137
|
+
return None
|
138
|
+
# # Reuse the same tolerant parsing as is_remote_server
|
139
|
+
# if self.command != "npx" or "mcp-remote" not in self.args:
|
140
|
+
# return None
|
141
|
+
# try:
|
142
|
+
# # idx: int = self.args.index("mcp-remote")
|
143
|
+
# for candidate in self.args[:]:
|
144
|
+
# if candidate.startswith(("https://", "http://")):
|
145
|
+
# return candidate
|
146
|
+
# return None
|
147
|
+
# except Exception:
|
148
|
+
# return None
|
145
149
|
|
146
150
|
|
147
151
|
@dataclass
|
@@ -165,7 +169,7 @@ def load_json_file(path: Path) -> dict[str, Any]:
|
|
165
169
|
|
166
170
|
|
167
171
|
def clear_json_file_cache() -> None:
|
168
|
-
"""Clear the cache for the
|
172
|
+
"""Clear the cache for the JSON file loading"""
|
169
173
|
load_json_file.cache_clear()
|
170
174
|
|
171
175
|
|
@@ -26,12 +26,15 @@ def _approval_key(session_id: str, kind: str, name: str) -> str:
|
|
26
26
|
|
27
27
|
|
28
28
|
def requires_loop(func: Callable[..., Any]) -> Callable[..., None | Any]: # noqa: ANN401
|
29
|
-
"""Decorator to ensure the function is called when there is
|
29
|
+
"""Decorator to ensure the function is called when there is a running asyncio loop.
|
30
30
|
This is for sync(!) functions that return None / can do so on error"""
|
31
31
|
|
32
32
|
@wraps(func)
|
33
33
|
def wrapper(*args: Any, **kwargs: Any) -> None | Any:
|
34
|
-
|
34
|
+
try:
|
35
|
+
# get_running_loop() raises RuntimeError if no loop is running in this thread
|
36
|
+
_ = asyncio.get_running_loop()
|
37
|
+
except RuntimeError:
|
35
38
|
log.warning("fire_and_forget called in non-async context")
|
36
39
|
return None
|
37
40
|
return func(*args, **kwargs)
|
@@ -0,0 +1 @@
|
|
1
|
+
*,: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}.\!container{width:100%!important}.container{width:100%}@media (min-width: 640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width: 768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width: 1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width: 1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width: 1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.-inset-0\.5{top:-.125rem;right:-.125rem;bottom:-.125rem;left:-.125rem}.bottom-4{bottom:1rem}.left-4{left:1rem}.right-4{right:1rem}.right-auto{right:auto}.z-50{z-index:50}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.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}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[580px\]{height:580px}.w-10{width:2.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-\[260px\]{width:260px}.w-\[min\(92vw\,28rem\)\]{width:min(92vw,28rem)}.w-full{width:100%}.min-w-\[240px\]{min-width:240px}.max-w-\[1400px\]{max-width:1400px}.max-w-\[260px\]{max-width:260px}.max-w-sm{max-width:24rem}.border-collapse{border-collapse:collapse}.translate-x-1{--tw-translate-x: .25rem;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-x-5{--tw-translate-x: 1.25rem;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-\[-4px\]{--tw-translate-y: -4px;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))}.animate-\[pop_300ms_ease-out\]{animation:pop .3s ease-out}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.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-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-amber-400\/30{border-color:#fbbf244d}.border-app-accent{border-color:var(--accent)}.border-app-border{border-color:var(--border)}.border-blue-400\/30{border-color:#60a5fa4d}.border-blue-400\/60{border-color:#60a5fa99}.border-rose-400\/30{border-color:#fb71854d}.\!bg-blue-100{--tw-bg-opacity: 1 !important;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))!important}.\!bg-blue-600{--tw-bg-opacity: 1 !important;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))!important}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-app-accent{background-color:var(--accent)}.bg-app-bg{background-color:var(--bg)}.bg-app-border{background-color:var(--border)}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-400\/20{background-color:#60a5fa33}.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-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-rose-400{--tw-bg-opacity: 1;background-color:rgb(251 113 133 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.\!px-2{padding-left:.5rem!important;padding-right:.5rem!important}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.\!py-1\.5{padding-top:.375rem!important;padding-bottom:.375rem!important}.\!py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.align-bottom{vertical-align:bottom}.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-\[10px\]{font-size:10px}.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-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.\!text-white{--tw-text-opacity: 1 !important;color:rgb(255 255 255 / var(--tw-text-opacity, 1))!important}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-app-accent{color:var(--accent)}.text-app-muted{color:var(--muted)}.text-app-text{color:var(--text)}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-900{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / 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-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-rose-400{--tw-text-opacity: 1;color:rgb(251 113 133 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.accent-blue-500{accent-color:#3b82f6}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px 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-xl{--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)}.blur-md{--tw-blur: blur(12px);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)}.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-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-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}:root{--bg: #0b0c10;--card: #111318;--border: #1f2430;--text: #e6e6e6;--muted: #a0a7b4;--accent: #7c3aed;--success: #10b981;--warning: #f59e0b;--danger: #ef4444}[data-theme=dark]{--bg: #0b0c10;--card: #111318;--border: #1f2430;--text: #e6e6e6;--muted: #a0a7b4}[data-theme=light]{--bg: #f8fafc;--card: #ffffff;--border: #e5e7eb;--text: #0f172a;--muted: #475569}@media (prefers-color-scheme: light){:root{--bg: #f8fafc;--card: #ffffff;--border: #e5e7eb;--text: #0f172a;--muted: #475569}}html,body,#root{height:100%}body{margin:0;background:var(--bg);color:var(--text)}.container{margin:0 auto;padding:24px;max-width:1100px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px}.card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:16px;box-shadow:0 1px 2px #0000000a,0 2px 12px #00000014}.stat{display:flex;align-items:center;gap:12px}.badge{display:inline-block;font-size:12px;padding:2px 8px;border-radius:999px;border:1px solid var(--border);background:#7c3aed14;color:var(--text)}.table{width:100%;border-collapse:collapse}.table th,.table td{border-bottom:1px solid var(--border);padding:8px 4px;text-align:left}.muted{color:var(--muted)}.accent{color:var(--accent)}.success{color:var(--success)}.warning{color:var(--warning)}.danger{color:var(--danger)}.toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px}.button{border:1px solid var(--border);background:var(--card);color:var(--text);padding:6px 10px;border-radius:8px;cursor:pointer}.button:hover{filter:brightness(1.05)}.hover\:\!bg-blue-700:hover{--tw-bg-opacity: 1 !important;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))!important}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--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-visible\:ring-blue-400:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(96 165 250 / var(--tw-ring-opacity, 1))}@media (min-width: 640px){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-\[220px_1fr\]{grid-template-columns:220px 1fr}}@media (prefers-color-scheme: dark){.dark\:\!bg-blue-800\/40{background-color:#1e40af66!important}.dark\:bg-blue-900\/20{background-color:#1e3a8a33}.dark\:text-blue-200{--tw-text-opacity: 1;color:rgb(191 219 254 / var(--tw-text-opacity, 1))}}
|