mh-local 0.1.0a1__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.
Files changed (29) hide show
  1. mh_local-0.1.0a1/PKG-INFO +77 -0
  2. mh_local-0.1.0a1/README.md +68 -0
  3. mh_local-0.1.0a1/pyproject.toml +30 -0
  4. mh_local-0.1.0a1/src/mh_local/__init__.py +0 -0
  5. mh_local-0.1.0a1/src/mh_local/__main__.py +3 -0
  6. mh_local-0.1.0a1/src/mh_local/app.py +238 -0
  7. mh_local-0.1.0a1/src/mh_local/auth.py +29 -0
  8. mh_local-0.1.0a1/src/mh_local/main.py +85 -0
  9. mh_local-0.1.0a1/src/mh_local/static/assets/AgentManagementPage-BJt2TpK1.css +1 -0
  10. mh_local-0.1.0a1/src/mh_local/static/assets/AgentManagementPage-CXjYDSjB.js +1 -0
  11. mh_local-0.1.0a1/src/mh_local/static/assets/ManagementNav-B9XyX5Un.js +1 -0
  12. mh_local-0.1.0a1/src/mh_local/static/assets/ManagementNav-_BEf98ne.css +1 -0
  13. mh_local-0.1.0a1/src/mh_local/static/assets/ProviderManagementPage-BNB9wWWv.js +1 -0
  14. mh_local-0.1.0a1/src/mh_local/static/assets/ProviderManagementPage-DBI_1RWV.css +1 -0
  15. mh_local-0.1.0a1/src/mh_local/static/assets/SceneManagementPage-BUbMRw_e.css +1 -0
  16. mh_local-0.1.0a1/src/mh_local/static/assets/SceneManagementPage-CA7Ob7Hu.js +1 -0
  17. mh_local-0.1.0a1/src/mh_local/static/assets/ToolManagementPage-5re12IWS.js +1 -0
  18. mh_local-0.1.0a1/src/mh_local/static/assets/ToolManagementPage-Cazlqqda.css +1 -0
  19. mh_local-0.1.0a1/src/mh_local/static/assets/index-C1ORYu0Z.css +1 -0
  20. mh_local-0.1.0a1/src/mh_local/static/assets/index-D8sObRrZ.js +115 -0
  21. mh_local-0.1.0a1/src/mh_local/static/component/mh-extra-components.umd.js +51 -0
  22. mh_local-0.1.0a1/src/mh_local/static/component/mh-tool-components.umd.js +1 -0
  23. mh_local-0.1.0a1/src/mh_local/static/favicon.svg +3 -0
  24. mh_local-0.1.0a1/src/mh_local/static/index.html +14 -0
  25. mh_local-0.1.0a1/src/mh_local/storage/__init__.py +0 -0
  26. mh_local-0.1.0a1/src/mh_local/storage/paths.py +56 -0
  27. mh_local-0.1.0a1/src/mh_local/storage/provider_store.py +92 -0
  28. mh_local-0.1.0a1/src/mh_local/storage/registry_store.py +561 -0
  29. mh_local-0.1.0a1/src/mh_local/storage/session_store.py +254 -0
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.3
2
+ Name: mh-local
3
+ Version: 0.1.0a1
4
+ Summary: Minimal Harness Client — pure local Agent assistant
5
+ Requires-Dist: uvicorn[standard]>=0.30.0
6
+ Requires-Dist: mh-gateway==0.1.0a1
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+
10
+ # mh-local
11
+
12
+ A self-contained local Agent assistant powered by [minimal-harness](https://github.com/J0ey1iu/minimal-harness). It bundles a FastAPI backend with a built-in Vue 3 SPA frontend — no database, no external service dependencies.
13
+
14
+ Latest version: **0.1.0a1**
15
+
16
+ ## Features
17
+
18
+ - **No cloud required** — runs entirely on localhost, single-user
19
+ - **Built-in web UI** — chat interface with agent, scene, tool, and LLM provider management
20
+ - **No database** — file-based sessions and registries under `~/.config/mh-local/`
21
+ - **Auto-discovery** — automatically creates a workspace scene from the current directory
22
+
23
+ ## Quick start
24
+
25
+ ```bash
26
+ uv add mh-local
27
+ uv run mh-local
28
+ ```
29
+
30
+ Your browser will open automatically to `http://127.0.0.1:<port>`.
31
+
32
+ On first run, you'll be prompted to configure an LLM provider (API key, base URL) via the web UI's Provider management page.
33
+
34
+ ## Usage
35
+
36
+ After starting, mh-local serves a full-featured chat UI at the local URL with:
37
+
38
+ - **Chat** — converse with AI agents with streaming responses
39
+ - **Manage Agents** — view and configure built-in agents (General Assistant, Code Reviewer, Writer)
40
+ - **Manage Tools** — inspect available tools (bash, handoff, discover_agents, and more)
41
+ - **Manage Scenes** — workspace management for different projects
42
+ - **Manage Providers** — configure LLM providers (OpenAI, Anthropic, etc.)
43
+
44
+ ## How it works
45
+
46
+ ```
47
+ mh-local
48
+ ├── FastAPI app (mh_gateway-based) — serves API + static files
49
+ ├── Vue 3 SPA — chat UI served at /
50
+ ├── File-based stores:
51
+ │ ├── sessions/ — conversation history
52
+ │ ├── providers.json — LLM provider config
53
+ │ └── registry.json — agents, tools, scenes
54
+ └── Local auth — auto-grants admin access
55
+ ```
56
+
57
+ ## Configuration
58
+
59
+ All configuration is done through the web UI. Persistent data lives at:
60
+
61
+ | Path | Contents |
62
+ |------|----------|
63
+ | `~/.config/mh-local/providers.json` | LLM provider credentials |
64
+ | `~/.config/mh-local/registry.json` | Agent/tool/scene registrations |
65
+ | `~/.config/mh-local/sessions/` | Chat session files |
66
+ | `~/.config/mh-local/logs/` | Runtime logs |
67
+
68
+ ## Development
69
+
70
+ ```bash
71
+ # Build frontend (SPA + component UMD bundles)
72
+ powershell scripts/build-mh-local-frontend.ps1 # Windows
73
+ bash scripts/build-mh-local-frontend.sh # Linux/macOS
74
+
75
+ # Run
76
+ uv run mh-local
77
+ ```
@@ -0,0 +1,68 @@
1
+ # mh-local
2
+
3
+ A self-contained local Agent assistant powered by [minimal-harness](https://github.com/J0ey1iu/minimal-harness). It bundles a FastAPI backend with a built-in Vue 3 SPA frontend — no database, no external service dependencies.
4
+
5
+ Latest version: **0.1.0a1**
6
+
7
+ ## Features
8
+
9
+ - **No cloud required** — runs entirely on localhost, single-user
10
+ - **Built-in web UI** — chat interface with agent, scene, tool, and LLM provider management
11
+ - **No database** — file-based sessions and registries under `~/.config/mh-local/`
12
+ - **Auto-discovery** — automatically creates a workspace scene from the current directory
13
+
14
+ ## Quick start
15
+
16
+ ```bash
17
+ uv add mh-local
18
+ uv run mh-local
19
+ ```
20
+
21
+ Your browser will open automatically to `http://127.0.0.1:<port>`.
22
+
23
+ On first run, you'll be prompted to configure an LLM provider (API key, base URL) via the web UI's Provider management page.
24
+
25
+ ## Usage
26
+
27
+ After starting, mh-local serves a full-featured chat UI at the local URL with:
28
+
29
+ - **Chat** — converse with AI agents with streaming responses
30
+ - **Manage Agents** — view and configure built-in agents (General Assistant, Code Reviewer, Writer)
31
+ - **Manage Tools** — inspect available tools (bash, handoff, discover_agents, and more)
32
+ - **Manage Scenes** — workspace management for different projects
33
+ - **Manage Providers** — configure LLM providers (OpenAI, Anthropic, etc.)
34
+
35
+ ## How it works
36
+
37
+ ```
38
+ mh-local
39
+ ├── FastAPI app (mh_gateway-based) — serves API + static files
40
+ ├── Vue 3 SPA — chat UI served at /
41
+ ├── File-based stores:
42
+ │ ├── sessions/ — conversation history
43
+ │ ├── providers.json — LLM provider config
44
+ │ └── registry.json — agents, tools, scenes
45
+ └── Local auth — auto-grants admin access
46
+ ```
47
+
48
+ ## Configuration
49
+
50
+ All configuration is done through the web UI. Persistent data lives at:
51
+
52
+ | Path | Contents |
53
+ |------|----------|
54
+ | `~/.config/mh-local/providers.json` | LLM provider credentials |
55
+ | `~/.config/mh-local/registry.json` | Agent/tool/scene registrations |
56
+ | `~/.config/mh-local/sessions/` | Chat session files |
57
+ | `~/.config/mh-local/logs/` | Runtime logs |
58
+
59
+ ## Development
60
+
61
+ ```bash
62
+ # Build frontend (SPA + component UMD bundles)
63
+ powershell scripts/build-mh-local-frontend.ps1 # Windows
64
+ bash scripts/build-mh-local-frontend.sh # Linux/macOS
65
+
66
+ # Run
67
+ uv run mh-local
68
+ ```
@@ -0,0 +1,30 @@
1
+ [project]
2
+ name = "mh-local"
3
+ version = "0.1.0a1"
4
+ description = "Minimal Harness Client — pure local Agent assistant"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "uvicorn[standard]>=0.30.0",
9
+ "mh-gateway==0.1.0a1",
10
+ ]
11
+
12
+ [project.scripts]
13
+ mh-local = "mh_local.main:run"
14
+
15
+ [build-system]
16
+ requires = ["uv_build>=0.10.12,<0.12"]
17
+ build-backend = "uv_build"
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["src"]
21
+
22
+ [tool.setuptools.package-data]
23
+ mh_local = ["static/**/*"]
24
+
25
+ [tool.uv.sources]
26
+ mh-gateway = { workspace = true }
27
+
28
+ [[tool.uv.index]]
29
+ url = "https://mirrors.aliyun.com/pypi/simple/"
30
+ default = true
File without changes
@@ -0,0 +1,3 @@
1
+ from mh_local.main import run
2
+
3
+ run()
@@ -0,0 +1,238 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from collections.abc import AsyncIterator
5
+ from contextlib import asynccontextmanager
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from fastapi import FastAPI
10
+ from fastapi.staticfiles import StaticFiles
11
+ from mh_gateway.adapters import DatabaseProtocol
12
+ from mh_gateway.app import create_app
13
+ from mh_gateway.config import ConfigSchema
14
+ from minimal_harness.llm.factory import register_builtin_providers
15
+ from minimal_harness.llm.llm import LLMProvider, ProviderFactory
16
+ from starlette.responses import FileResponse
17
+
18
+ from mh_local.auth import LocalAuthProvider
19
+ from mh_local.storage.provider_store import FileLLMProviderStore
20
+ from mh_local.storage.registry_store import FileManagementProvider
21
+ from mh_local.storage.session_store import JSONLSessionStore
22
+
23
+ logger = logging.getLogger("mh_local")
24
+
25
+
26
+ class _NullDatabase(DatabaseProtocol):
27
+ async def init(self, dsn: str) -> None:
28
+ pass
29
+
30
+ async def close(self) -> None:
31
+ pass
32
+
33
+ async def execute(self, sql: str, params: list | None = None) -> Any:
34
+ return None
35
+
36
+ async def execute_write(self, sql: str, params: list | None = None) -> int:
37
+ return 0
38
+
39
+ async def fetch_one(self, sql: str, params: list | None = None) -> dict | None:
40
+ return {"ok": 1}
41
+
42
+ async def fetch_all(self, sql: str, params: list | None = None) -> list[dict]:
43
+ return []
44
+
45
+ async def begin(self) -> None:
46
+ pass
47
+
48
+ async def commit(self) -> None:
49
+ pass
50
+
51
+ async def rollback(self) -> None:
52
+ pass
53
+
54
+ async def executemany(self, sql: str, params_list: list[list]) -> None:
55
+ pass
56
+
57
+
58
+ class _M2MAuth:
59
+ async def close(self) -> None:
60
+ pass
61
+
62
+ async def authenticate(self, request: Any) -> str | None:
63
+ return "local"
64
+
65
+ async def get_identity_headers(self, request: Any, identity: str) -> dict[str, str]:
66
+ return {}
67
+
68
+
69
+ class _OutboundAuth:
70
+ async def close(self) -> None:
71
+ pass
72
+
73
+ async def get_headers(
74
+ self, request: Any, target_url: str, target_type: str
75
+ ) -> dict[str, str]:
76
+ return {}
77
+
78
+
79
+ def create_local_app() -> FastAPI:
80
+ settings = ConfigSchema(
81
+ dev_mode=False,
82
+ log_level="INFO",
83
+ db_path="",
84
+ db_auto_schema=False,
85
+ enable_eval=False,
86
+ metrics_enabled=False,
87
+ cors_origins=[],
88
+ eval_results_dir="",
89
+ )
90
+
91
+ auth = LocalAuthProvider()
92
+ session_store = JSONLSessionStore()
93
+ provider_store = FileLLMProviderStore()
94
+ management = FileManagementProvider()
95
+
96
+ @asynccontextmanager
97
+ async def _auth_hook(app: FastAPI) -> AsyncIterator[None]:
98
+ app.state.adapters.token_verifier = auth
99
+ app.state.adapters.permission_checker = auth
100
+ yield
101
+
102
+ @asynccontextmanager
103
+ async def _database_hook(app: FastAPI) -> AsyncIterator[None]:
104
+ app.state.adapters.database_provider = _NullDatabase()
105
+ yield
106
+
107
+ @asynccontextmanager
108
+ async def _session_store_hook(app: FastAPI) -> AsyncIterator[None]:
109
+ app.state.adapters.session_store_provider = session_store
110
+ yield
111
+
112
+ @asynccontextmanager
113
+ async def _management_hook(app: FastAPI) -> AsyncIterator[None]:
114
+ await management.sync_cwd_scene()
115
+ app.state.adapters.management_provider = management
116
+ yield
117
+
118
+ @asynccontextmanager
119
+ async def _llm_registry_hook(app: FastAPI) -> AsyncIterator[None]:
120
+ factory = ProviderFactory()
121
+ register_builtin_providers(factory)
122
+ app.state.adapters.llm_provider_registry = factory
123
+
124
+ def _default_factory() -> LLMProvider:
125
+ return factory.create("openai", {})
126
+
127
+ app.state.adapters.llm_provider_factory = _default_factory
128
+ yield
129
+
130
+ @asynccontextmanager
131
+ async def _provider_store_hook(app: FastAPI) -> AsyncIterator[None]:
132
+ app.state.adapters.llm_provider_store = provider_store
133
+ yield
134
+ await provider_store.close()
135
+
136
+ @asynccontextmanager
137
+ async def _m2m_hook(app: FastAPI) -> AsyncIterator[None]:
138
+ app.state.adapters.m2m_auth_provider = _M2MAuth()
139
+ yield
140
+
141
+ @asynccontextmanager
142
+ async def _outbound_hook(app: FastAPI) -> AsyncIterator[None]:
143
+ app.state.adapters.outbound_auth_provider = _OutboundAuth()
144
+ yield
145
+
146
+ app = create_app(
147
+ settings=settings,
148
+ token_verifier=_auth_hook,
149
+ permission_checker=None,
150
+ management_provider=_management_hook,
151
+ llm_provider_registry=_llm_registry_hook,
152
+ llm_provider_store=_provider_store_hook,
153
+ outbound_auth_provider=_outbound_hook,
154
+ m2m_auth_provider=_m2m_hook,
155
+ database_provider=_database_hook,
156
+ session_store_provider=_session_store_hook,
157
+ lifespan_hooks=None,
158
+ dev_routers=None,
159
+ )
160
+
161
+ _mount_mh_local_static(app)
162
+ return app
163
+
164
+
165
+ def _mount_mh_local_static(app: FastAPI) -> None:
166
+ static_dir = Path(__file__).resolve().parent / "static"
167
+ if not static_dir.is_dir():
168
+ logger.warning("MH-Local static directory not found at %s", static_dir)
169
+ return
170
+
171
+ subdirs = [
172
+ ("/assets", static_dir / "assets"),
173
+ ("/component", static_dir / "component"),
174
+ ]
175
+ for url_path, fs_path in subdirs:
176
+ if fs_path.is_dir():
177
+ app.mount(
178
+ url_path,
179
+ StaticFiles(directory=str(fs_path)),
180
+ name=f"mh_local_{url_path.lstrip('/')}",
181
+ )
182
+
183
+ favicon = static_dir / "favicon.svg"
184
+ if favicon.is_file():
185
+ from fastapi.responses import FileResponse as FR
186
+
187
+ @app.get("/favicon.svg", include_in_schema=False)
188
+ async def _favicon():
189
+ return FR(str(favicon))
190
+
191
+ @app.middleware("http")
192
+ async def _mh_local_spa_fallback(request: Any, call_next: Any) -> Any:
193
+ response = await call_next(request)
194
+ if (
195
+ response.status_code == 404
196
+ and request.method == "GET"
197
+ and request.url.path != "/api"
198
+ and not request.url.path.startswith("/api/")
199
+ and not request.url.path.startswith("/docs")
200
+ and not request.url.path.startswith("/openapi")
201
+ and not request.url.path.startswith("/redoc")
202
+ and not request.url.path.startswith("/assets")
203
+ and not request.url.path.startswith("/component")
204
+ ):
205
+ index_path = static_dir / "index.html"
206
+ if index_path.is_file():
207
+ return FileResponse(str(index_path))
208
+ return response
209
+
210
+ _mount_component_sources(app)
211
+
212
+
213
+ def _mount_component_sources(app: FastAPI) -> None:
214
+ static_dir = Path(__file__).resolve().parent / "static"
215
+ components = static_dir / "component"
216
+ if not components.is_dir():
217
+ return
218
+
219
+ from fastapi import APIRouter
220
+
221
+ router = APIRouter(prefix="/api/v1/component-sources", tags=["component-sources"])
222
+
223
+ sources = []
224
+ for f in components.iterdir():
225
+ if f.suffix == ".js":
226
+ sources.append(
227
+ {
228
+ "id": f.stem,
229
+ "label": f.stem.replace("mh-", "").replace("-", " ").title(),
230
+ "url": f"/component/{f.name}",
231
+ }
232
+ )
233
+
234
+ @router.get("")
235
+ async def get_component_sources():
236
+ return sources
237
+
238
+ app.include_router(router)
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from mh_gateway.adapters import (
6
+ PermissionChecker,
7
+ UserAuthProvider,
8
+ UserIdentity,
9
+ )
10
+
11
+
12
+ class LocalAuthProvider(UserAuthProvider, PermissionChecker):
13
+ async def verify(self, request: Any) -> UserIdentity:
14
+ return UserIdentity(
15
+ user_id="local",
16
+ username="Local User",
17
+ roles=["admin"],
18
+ )
19
+
20
+ async def logout(self, request: Any, response: Any) -> None:
21
+ response.set_cookie(
22
+ key="x-user-id", value="", httponly=True, max_age=0, path="/"
23
+ )
24
+
25
+ async def get_permissions(self, user_id: str) -> list[str]:
26
+ return ["*", "*:*:*"]
27
+
28
+ async def check(self, user_id: str, permission: str) -> bool:
29
+ return True
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ import socket
7
+ import sys
8
+ import webbrowser
9
+ from logging.handlers import TimedRotatingFileHandler
10
+
11
+ import uvicorn
12
+
13
+ from mh_local.app import create_local_app
14
+ from mh_local.storage.paths import LOGS_DIR, PROVIDERS_FILE, ensure_dirs
15
+ from mh_local.storage.registry_store import compute_scene_id
16
+
17
+ logger = logging.getLogger("mh_local")
18
+
19
+
20
+ def _find_free_port() -> int:
21
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
22
+ s.bind(("127.0.0.1", 0))
23
+ return s.getsockname()[1]
24
+
25
+
26
+ def _has_providers() -> bool:
27
+ if not PROVIDERS_FILE.exists():
28
+ return False
29
+ data = json.loads(PROVIDERS_FILE.read_text("utf-8"))
30
+ return any(p.get("api_key", "").strip() for p in data)
31
+
32
+
33
+ def _open_url(port: int) -> str:
34
+ cwd = os.getcwd()
35
+ scene_id = compute_scene_id(cwd)
36
+ url = f"http://127.0.0.1:{port}/#/?scene={scene_id}"
37
+ webbrowser.open(url)
38
+ return url
39
+
40
+
41
+ def run() -> None:
42
+ ensure_dirs()
43
+
44
+ log_file = LOGS_DIR / "mh_local.log"
45
+ file_handler = TimedRotatingFileHandler(
46
+ str(log_file), when="midnight", interval=1, backupCount=7, encoding="utf-8"
47
+ )
48
+ file_handler.setFormatter(
49
+ logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s")
50
+ )
51
+
52
+ logging.basicConfig(
53
+ level=logging.INFO,
54
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
55
+ handlers=[logging.StreamHandler(sys.stderr), file_handler],
56
+ )
57
+ logging.getLogger("uvicorn.access").handlers.clear()
58
+ logging.getLogger("uvicorn.access").propagate = False
59
+
60
+ first_run = not _has_providers()
61
+ if first_run:
62
+ logger.info(
63
+ "No LLM provider configured. "
64
+ "A template has been created at %s. "
65
+ "Please configure your API key via the web UI (Manage -> Providers).",
66
+ PROVIDERS_FILE,
67
+ )
68
+
69
+ app = create_local_app()
70
+ port = _find_free_port()
71
+
72
+ url = _open_url(port)
73
+ logger.info("Starting mh-local on %s", url)
74
+
75
+ uvicorn.run(
76
+ app,
77
+ host="127.0.0.1",
78
+ port=port,
79
+ log_level="info",
80
+ access_log=False,
81
+ )
82
+
83
+
84
+ if __name__ == "__main__":
85
+ run()
@@ -0,0 +1 @@
1
+ .ta-messages[data-v-3af0e115]{flex:1;overflow-y:auto;margin-bottom:12px;display:flex;flex-direction:column;gap:10px;min-height:200px;max-height:400px}.ta-empty[data-v-3af0e115]{color:var(--text-muted);font-size:13px;text-align:center;padding:48px 16px}.ta-msg[data-v-3af0e115]{padding:10px 12px;border-radius:10px;font-size:13px;line-height:1.5}.ta-msg-user[data-v-3af0e115]{background:var(--accent-dim);align-self:flex-end;max-width:85%}.ta-msg-assistant[data-v-3af0e115]{background:var(--glass-highlight);border:1px solid var(--glass-border);align-self:flex-start;max-width:85%}.ta-msg-label[data-v-3af0e115]{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.3px;color:var(--text-secondary);margin-bottom:4px}.ta-msg-content[data-v-3af0e115]{white-space:pre-wrap;word-break:break-word}.ta-msg-thinking[data-v-3af0e115]{color:var(--text-muted);font-style:italic}.ta-input-row[data-v-3af0e115]{display:flex;gap:8px}.ta-input[data-v-3af0e115]{flex:1;padding:9px 12px;background:var(--glass-highlight);border:1px solid var(--glass-border);border-radius:8px;color:var(--text-primary);font-size:13px;box-sizing:border-box;transition:border-color .15s}.ta-input[data-v-3af0e115]:focus{outline:none;border-color:var(--accent)}.ta-input[data-v-3af0e115]:disabled{opacity:.5}.btn-send[data-v-3af0e115]{padding:9px 18px;background:var(--accent);border:none;border-radius:8px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;transition:opacity .15s;white-space:nowrap}.btn-send[data-v-3af0e115]:hover:not(:disabled){opacity:.9}.btn-send[data-v-3af0e115]:disabled{opacity:.5;cursor:not-allowed}
@@ -0,0 +1 @@
1
+ import{n as de,Q as ce,O as me,y as ue,z as r,g as f,m as O,d as e,I as o,J as s,Z as m,W as u,$ as _e,F as K,E as Q,S as W,e as q,a0 as Z,l as g,f as G,T as H,x as re,c as R,A as c,t as pe,s as ve,K as ge,h as he,o as fe,_ as ye}from"./index-D8sObRrZ.js";import{M as be}from"./ManagementNav-B9XyX5Un.js";const ze={class:"mgmt-page"},we={class:"mgmt-page-content"},ke={class:"mgmt-header"},Ce={class:"mgmt-toolbar"},Ve=["placeholder"],xe={key:0,class:"table-wrap"},Se={class:"mgmt-table"},Ue={key:1,class:"mgmt-empty"},$e={key:2},Me={class:"table-wrap"},Fe={class:"mgmt-table"},Ne=["title"],Ae=["title"],Te=["title"],Je=["title"],Oe=["title"],Pe={class:"cell-actions"},Le=["onClick"],Be=["onClick"],Ie={class:"mgmt-pagination"},De=["disabled"],Ee={class:"mgmt-page-info"},Ke=["disabled"],Qe={class:"dialog dialog-wide"},We={class:"form-group"},qe=["disabled","placeholder"],Ze={class:"form-group"},Ge=["placeholder"],He={class:"form-group"},Re={class:"tc-label-row"},Xe=["title"],Ye={class:"form-group"},je={class:"tc-label-row"},el=["title"],ll={class:"form-group"},tl={class:"form-group"},sl=["placeholder"],al={class:"locale-section"},ol={class:"form-group"},nl={class:"tc-label-row"},il=["title"],dl=["placeholder"],cl={class:"locale-section"},ml={class:"locale-field"},ul={class:"locale-field-label"},_l={class:"locale-input-row"},rl={class:"locale-lang"},pl={class:"locale-lang"},vl=["placeholder"],gl={class:"locale-field"},hl={class:"locale-field-label"},fl={class:"locale-input-row"},yl={class:"locale-lang"},bl=["title"],zl={class:"locale-lang"},wl=["title"],kl=["placeholder"],Cl={class:"locale-field"},Vl={class:"locale-field-label"},xl={class:"locale-input-row"},Sl={class:"locale-lang"},Ul=["title"],$l={class:"locale-lang"},Ml=["title"],Fl=["placeholder"],Nl={class:"dialog-actions"},Al=["disabled"],Tl={class:"tc-overlay-content"},Jl={class:"tc-overlay-header"},Ol={class:"tc-overlay-title"},Pl={class:"tc-overlay-body"},Ll=de({__name:"AgentManagementPage",setup(Bl){const{t:a,localeVal:V}=ce(),k=me(),M=c([]),X=R(()=>M.value.map(n=>({value:n.name,label:n.name}))),F=c([]),N=c(!1),z=c(!1),C=c(!1),d=c({name:"",display_name:"",display_name_locale:"",description:"",description_locale:"",system_prompt:"",system_prompt_locale:"",provider:"openai",model:"",llm_config:{}}),i=c({display_zh:"",display_en:"",desc_zh:"",desc_en:"",prompt_zh:"",prompt_en:""});function A(n){if(!n)return{zh:"",en:""};try{const l=JSON.parse(n);return{zh:l.zh??"",en:l.en??""}}catch{return{zh:"",en:""}}}function T(n,l){return!n&&!l?"":JSON.stringify({zh:n,en:l})}function Y(){const n=A(d.value.display_name_locale);i.value.display_zh=n.zh,i.value.display_en=n.en;const l=A(d.value.description_locale);i.value.desc_zh=l.zh,i.value.desc_en=l.en;const v=A(d.value.system_prompt_locale);i.value.prompt_zh=v.zh,i.value.prompt_en=v.en}function j(){d.value.display_name_locale=T(i.value.display_zh,i.value.display_en),d.value.description_locale=T(i.value.desc_zh,i.value.desc_en),d.value.system_prompt_locale=T(i.value.prompt_zh,i.value.prompt_en)}const x=c(""),h=c(1),S=c(15),P=c(0),ee=[{value:10,label:"10"},{value:15,label:"15"},{value:20,label:"20"},{value:50,label:"50"}],L=R(()=>Math.max(1,Math.ceil(P.value/S.value))),U=c(!1),y=c(""),J=c(!1),B=c(""),p=c("");let _=null;function b(n,l,v){B.value=n,p.value=l,_=v,J.value=!0}function I(){_==="description"?d.value.description=p.value:_==="system_prompt"?d.value.system_prompt=p.value:_==="llm_config"?y.value=p.value:_==="locale_desc_zh"?i.value.desc_zh=p.value:_==="locale_desc_en"?i.value.desc_en=p.value:_==="locale_prompt_zh"?i.value.prompt_zh=p.value:_==="locale_prompt_en"&&(i.value.prompt_en=p.value),J.value=!1,_=null}async function le(){try{const n=await pe({page_size:100});M.value=n.items}catch{M.value=[{name:"openai",provider_type:"openai"},{name:"anthropic",provider_type:"anthropic"}]}}async function w(){N.value=!0;try{const n=await ve({q:x.value,page:h.value,page_size:S.value});F.value=n.items,P.value=n.total}catch(n){k.show("Failed to load agents: "+n.message)}finally{N.value=!1}}function D(){h.value=1,w()}function E(n){h.value=n,w()}function te(){h.value=1,w()}function se(){C.value=!1,d.value={name:"",display_name:"",display_name_locale:"",description:"",description_locale:"",system_prompt:"",system_prompt_locale:"",provider:"openai",model:"",llm_config:{}},i.value={display_zh:"",display_en:"",desc_zh:"",desc_en:"",prompt_zh:"",prompt_en:""},y.value="",z.value=!0}function ae(n){C.value=!0,d.value={...n},y.value=n.llm_config?JSON.stringify(n.llm_config,null,2):"",Y(),z.value=!0}async function oe(){if(j(),y.value.trim())try{d.value.llm_config=JSON.parse(y.value)}catch{k.show("Invalid JSON in model config");return}else d.value.llm_config={};U.value=!0;try{C.value&&d.value.name?await ge(d.value.name,d.value):await he(d.value),z.value=!1,await w()}catch(n){k.show("Failed to save: "+n.message)}finally{U.value=!1}}function $(n,l){if(!n)return"-";const v=n.replace("T"," ").substring(0,16);return l?`${v} by ${l}`:v}async function ne(n){if(await k.confirm(a("alert_confirm_delete_agent",{name:n})))try{await fe(n),await w()}catch(l){k.show("Failed to delete: "+l.message)}}return ue(()=>{le(),w()}),(n,l)=>{var v;return r(),f("div",ze,[O(be),e("div",we,[e("header",ke,[e("h1",null,o(s(a)("mgmt_agents")),1),e("button",{class:"btn-primary",onClick:se},o(s(a)("mgmt_new_agent")),1)]),e("div",null,[e("div",Ce,[m(e("input",{"onUpdate:modelValue":l[0]||(l[0]=t=>x.value=t),class:"mgmt-search",placeholder:s(a)("mgmt_search_placeholder"),onKeyup:_e(D,["enter"])},null,40,Ve),[[u,x.value]]),e("button",{class:"btn-search",onClick:D},o(s(a)("mgmt_search")),1)]),N.value?(r(),f("div",xe,[e("table",Se,[e("thead",null,[e("tr",null,[e("th",null,o(s(a)("mgmt_name")),1),e("th",null,o(s(a)("mgmt_display_name")),1),e("th",null,o(s(a)("mgmt_description")),1),e("th",null,o(s(a)("mgmt_created_at")),1),e("th",null,o(s(a)("mgmt_updated_at")),1),e("th",null,o(s(a)("mgmt_actions")),1)])]),e("tbody",null,[(r(),f(K,null,Q(5,t=>e("tr",{key:t,class:"mgmt-skeleton-row"},[...l[27]||(l[27]=[e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"60%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"70%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell mgmt-skeleton-cell-sm",style:{width:"65%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell mgmt-skeleton-cell-sm",style:{width:"65%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1)])])),64))])])])):F.value.length===0?(r(),f("div",Ue,o(x.value?s(a)("mgmt_no_results"):s(a)("mgmt_no_agents")),1)):(r(),f("div",$e,[e("div",Me,[e("table",Fe,[e("thead",null,[e("tr",null,[e("th",null,o(s(a)("mgmt_name")),1),e("th",null,o(s(a)("mgmt_display_name")),1),e("th",null,o(s(a)("mgmt_description")),1),e("th",null,o(s(a)("mgmt_created_at")),1),e("th",null,o(s(a)("mgmt_updated_at")),1),e("th",null,o(s(a)("mgmt_actions")),1)])]),e("tbody",null,[(r(!0),f(K,null,Q(F.value,t=>(r(),f("tr",{key:t.name},[e("td",{title:t.name},[e("code",null,o(t.name),1)],8,Ne),e("td",{title:s(V)(t.display_name_locale,t.display_name)},o(s(V)(t.display_name_locale,t.display_name)),9,Ae),e("td",{class:"cell-desc",title:s(V)(t.description_locale,t.description)},o(s(V)(t.description_locale,t.description)),9,Te),e("td",{class:"cell-audit",title:$(t.created_at,t.created_by)},o($(t.created_at,t.created_by)),9,Je),e("td",{class:"cell-audit",title:$(t.updated_at,t.updated_by)},o($(t.updated_at,t.updated_by)),9,Oe),e("td",Pe,[e("button",{class:"btn-action",onClick:ie=>ae(t)},o(s(a)("mgmt_edit")),9,Le),e("button",{class:"btn-action btn-danger",onClick:ie=>ne(t.name)},o(s(a)("mgmt_delete")),9,Be)])]))),128))])])]),e("div",Ie,[e("button",{class:"btn-page",disabled:h.value<=1,onClick:l[1]||(l[1]=t=>E(h.value-1))},o(s(a)("mgmt_prev_page")),9,De),e("span",Ee,o(h.value)+" / "+o(L.value),1),e("button",{class:"btn-page",disabled:h.value>=L.value,onClick:l[2]||(l[2]=t=>E(h.value+1))},o(s(a)("mgmt_next_page")),9,Ke),O(W,{modelValue:S.value,"onUpdate:modelValue":l[3]||(l[3]=t=>S.value=t),modelModifiers:{number:!0},options:ee,searchable:!1,onChange:te},null,8,["modelValue"])])])),(r(),q(H,{to:"body"},[z.value?(r(),f("div",{key:0,class:"dialog-overlay",onClick:l[25]||(l[25]=Z(t=>z.value=!1,["self"]))},[e("div",Qe,[e("h2",null,o(C.value?s(a)("mgmt_edit_agent"):s(a)("mgmt_new_agent_title")),1),e("div",We,[e("label",null,o(s(a)("mgmt_name")),1),m(e("input",{"onUpdate:modelValue":l[4]||(l[4]=t=>d.value.name=t),disabled:C.value,placeholder:s(a)("mgmt_placeholder_id")},null,8,qe),[[u,d.value.name]])]),e("div",Ze,[e("label",null,o(s(a)("mgmt_display_name")),1),m(e("input",{"onUpdate:modelValue":l[5]||(l[5]=t=>d.value.display_name=t),placeholder:s(a)("mgmt_placeholder_display_name")},null,8,Ge),[[u,d.value.display_name]])]),e("div",He,[e("div",Re,[e("label",null,o(s(a)("mgmt_description")),1),e("button",{class:"tc-fullscreen-btn",onClick:l[6]||(l[6]=t=>b(s(a)("mgmt_description"),d.value.description??"","description")),title:s(a)("mgmt_tc_source_code")},"⛶",8,Xe)]),m(e("textarea",{"onUpdate:modelValue":l[7]||(l[7]=t=>d.value.description=t),rows:"2"},null,512),[[u,d.value.description]])]),e("div",Ye,[e("div",je,[e("label",null,o(s(a)("mgmt_system_prompt")),1),e("button",{class:"tc-fullscreen-btn",onClick:l[8]||(l[8]=t=>b(s(a)("mgmt_system_prompt"),d.value.system_prompt??"","system_prompt")),title:s(a)("mgmt_tc_source_code")},"⛶",8,el)]),m(e("textarea",{"onUpdate:modelValue":l[9]||(l[9]=t=>d.value.system_prompt=t),rows:"6",class:"mono"},null,512),[[u,d.value.system_prompt]])]),e("div",ll,[e("label",null,o(s(a)("mgmt_provider")),1),O(W,{modelValue:d.value.provider,"onUpdate:modelValue":l[10]||(l[10]=t=>d.value.provider=t),options:X.value,searchable:!1},null,8,["modelValue","options"])]),e("div",tl,[e("label",null,o(s(a)("mgmt_model")),1),m(e("input",{"onUpdate:modelValue":l[11]||(l[11]=t=>d.value.model=t),placeholder:s(a)("mgmt_model_placeholder")},null,8,sl),[[u,d.value.model]])]),e("details",al,[e("summary",null,o(s(a)("mgmt_llm_config")),1),e("div",ol,[e("div",nl,[l[28]||(l[28]=e("span",null,null,-1)),e("button",{class:"tc-fullscreen-btn",onClick:l[12]||(l[12]=t=>b(s(a)("mgmt_llm_config"),y.value,"llm_config")),title:s(a)("mgmt_tc_source_code")},"⛶",8,il)]),m(e("textarea",{"onUpdate:modelValue":l[13]||(l[13]=t=>y.value=t),rows:"4",class:"mono",placeholder:s(a)("mgmt_llm_config_placeholder")},null,8,dl),[[u,y.value]])])]),e("details",cl,[e("summary",null,o(s(a)("mgmt_translations")),1),e("div",ml,[e("div",ul,o(s(a)("mgmt_locale_display")),1),e("div",_l,[e("label",rl,[l[29]||(l[29]=g("zh ",-1)),m(e("input",{"onUpdate:modelValue":l[14]||(l[14]=t=>i.value.display_zh=t),placeholder:"中文名称"},null,512),[[u,i.value.display_zh]])]),e("label",pl,[l[30]||(l[30]=g("en ",-1)),m(e("input",{"onUpdate:modelValue":l[15]||(l[15]=t=>i.value.display_en=t),placeholder:s(a)("mgmt_placeholder_display_name")},null,8,vl),[[u,i.value.display_en]])])])]),e("div",gl,[e("div",hl,o(s(a)("mgmt_locale_desc")),1),e("div",fl,[e("label",yl,[l[31]||(l[31]=g("zh ",-1)),e("button",{class:"tc-fullscreen-btn",onClick:l[16]||(l[16]=t=>b("zh "+s(a)("mgmt_locale_desc"),i.value.desc_zh,"locale_desc_zh")),title:s(a)("mgmt_tc_source_code")},"⛶",8,bl),l[32]||(l[32]=g()),m(e("textarea",{"onUpdate:modelValue":l[17]||(l[17]=t=>i.value.desc_zh=t),rows:"2",placeholder:"中文描述"},null,512),[[u,i.value.desc_zh]])]),e("label",zl,[l[33]||(l[33]=g("en ",-1)),e("button",{class:"tc-fullscreen-btn",onClick:l[18]||(l[18]=t=>b("en "+s(a)("mgmt_locale_desc"),i.value.desc_en,"locale_desc_en")),title:s(a)("mgmt_tc_source_code")},"⛶",8,wl),l[34]||(l[34]=g()),m(e("textarea",{"onUpdate:modelValue":l[19]||(l[19]=t=>i.value.desc_en=t),rows:"2",placeholder:s(a)("mgmt_placeholder_desc")},null,8,kl),[[u,i.value.desc_en]])])])]),e("div",Cl,[e("div",Vl,o(s(a)("mgmt_locale_prompt")),1),e("div",xl,[e("label",Sl,[l[35]||(l[35]=g("zh ",-1)),e("button",{class:"tc-fullscreen-btn",onClick:l[20]||(l[20]=t=>b("zh "+s(a)("mgmt_locale_prompt"),i.value.prompt_zh,"locale_prompt_zh")),title:s(a)("mgmt_tc_source_code")},"⛶",8,Ul),l[36]||(l[36]=g()),m(e("textarea",{"onUpdate:modelValue":l[21]||(l[21]=t=>i.value.prompt_zh=t),rows:"3",placeholder:"中文系统提示"},null,512),[[u,i.value.prompt_zh]])]),e("label",$l,[l[37]||(l[37]=g("en ",-1)),e("button",{class:"tc-fullscreen-btn",onClick:l[22]||(l[22]=t=>b("en "+s(a)("mgmt_locale_prompt"),i.value.prompt_en,"locale_prompt_en")),title:s(a)("mgmt_tc_source_code")},"⛶",8,Ml),l[38]||(l[38]=g()),m(e("textarea",{"onUpdate:modelValue":l[23]||(l[23]=t=>i.value.prompt_en=t),rows:"3",placeholder:s(a)("mgmt_placeholder_prompt")},null,8,Fl),[[u,i.value.prompt_en]])])])])]),e("div",Nl,[e("button",{class:"btn-cancel",onClick:l[24]||(l[24]=t=>z.value=!1)},o(s(a)("mgmt_cancel")),1),e("button",{class:"btn-primary",disabled:U.value||!d.value.name,onClick:oe},o(U.value?s(a)("mgmt_saving"):s(a)("mgmt_save")),9,Al)])])])):G("",!0)])),(r(),q(H,{to:"body"},[J.value?(r(),f("div",{key:0,class:"tc-overlay",onClick:Z(I,["self"])},[e("div",Tl,[e("div",Jl,[e("span",Ol,o(B.value),1),e("div",{class:"tc-overlay-header-actions"},[e("button",{class:"tc-fullscreen-close",onClick:I},"✕")])]),e("div",Pl,[m(e("textarea",{"onUpdate:modelValue":l[26]||(l[26]=t=>p.value=t),class:re(s(_)==="system_prompt"||s(_)==="llm_config"||(v=s(_))!=null&&v.startsWith("locale_prompt")?"tc-overlay-textarea":"tc-overlay-textarea tc-overlay-textarea-text"),spellcheck:"false"},null,2),[[u,p.value]])])])])):G("",!0)]))])])])}}}),El=ye(Ll,[["__scopeId","data-v-3af0e115"]]);export{El as default};
@@ -0,0 +1 @@
1
+ import{n as I,R as P,U as H,P as R,H as y,Q as T,A as E,X as K,y as U,G as L,z as i,g as W,d as e,m as b,B as D,J as n,e as h,Y as d,I as c,x as m,f as g,S as G,c as v,_ as J}from"./index-D8sObRrZ.js";const O={class:"mgmt-nav"},Q={class:"mgmt-nav-inner"},X=["title"],Y={class:"nav-tabs"},$={class:"nav-controls"},q=I({__name:"ManagementNav",setup(F){const C=P(),B=H(),p=R(),{user:S}=y(p),x=T(),{t:o,setLocale:M}=x,{locale:w}=y(x);function k(s){var a;const t=(a=S.value)==null?void 0:a.permissions;return t?t.some(l=>l==="*"||l.startsWith(s)):!1}const A=v(()=>k("manage:scene:")),f=v(()=>k("manage:agent:")),z=v(()=>k("manage:tool:")),N=[{value:"light",labelKey:"theme_light"},{value:"dark",labelKey:"theme_dark"}],V=v(()=>N.map(s=>({value:s.value,label:o(s.labelKey)}))),r=E(localStorage.getItem("theme")||"light");function _(s){r.value=s,document.documentElement.setAttribute("data-theme",s),localStorage.setItem("theme",s)}K(r,_),U(async()=>{await p.checkAuth(),_(r.value)});function u(s){return C.path.startsWith(s)}function j(){M(w.value==="zh"?"en":"zh")}return(s,t)=>{const a=L("router-link");return i(),W("nav",O,[e("div",Q,[b(D),e("button",{class:"nav-back",onClick:t[0]||(t[0]=l=>n(B).back()),title:n(o)("mgmt_back")},[...t[2]||(t[2]=[e("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2.2","stroke-linecap":"round","stroke-linejoin":"round"},[e("path",{d:"M19 12H5M12 19l-7-7 7-7"})],-1)])],8,X),e("div",Y,[A.value?(i(),h(a,{key:0,to:"/manage/scenes",class:m(["nav-tab",{active:u("/manage/scenes")}])},{default:d(()=>[t[3]||(t[3]=e("svg",{width:"15",height:"15",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[e("rect",{x:"3",y:"3",width:"7",height:"7",rx:"1"}),e("rect",{x:"14",y:"3",width:"7",height:"7",rx:"1"}),e("rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}),e("rect",{x:"14",y:"14",width:"7",height:"7",rx:"1"})],-1)),e("span",null,c(n(o)("mgmt_scenes")),1)]),_:1},8,["class"])):g("",!0),f.value?(i(),h(a,{key:1,to:"/manage/agents",class:m(["nav-tab",{active:u("/manage/agents")}])},{default:d(()=>[t[4]||(t[4]=e("svg",{width:"15",height:"15",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[e("path",{d:"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"}),e("circle",{cx:"12",cy:"7",r:"4"})],-1)),e("span",null,c(n(o)("mgmt_agents")),1)]),_:1},8,["class"])):g("",!0),z.value?(i(),h(a,{key:2,to:"/manage/tools",class:m(["nav-tab",{active:u("/manage/tools")}])},{default:d(()=>[t[5]||(t[5]=e("svg",{width:"15",height:"15",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[e("path",{d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"})],-1)),e("span",null,c(n(o)("mgmt_tools")),1)]),_:1},8,["class"])):g("",!0),f.value?(i(),h(a,{key:3,to:"/manage/providers",class:m(["nav-tab",{active:u("/manage/providers")}])},{default:d(()=>[t[6]||(t[6]=e("svg",{width:"15",height:"15",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[e("rect",{x:"3",y:"3",width:"7",height:"7",rx:"1"}),e("rect",{x:"14",y:"3",width:"7",height:"7",rx:"1"}),e("rect",{x:"3",y:"14",width:"7",height:"7",rx:"1"}),e("rect",{x:"14",y:"14",width:"7",height:"7",rx:"1"})],-1)),e("span",null,c(n(o)("mgmt_providers")),1)]),_:1},8,["class"])):g("",!0)]),e("div",$,[b(G,{modelValue:r.value,"onUpdate:modelValue":t[1]||(t[1]=l=>r.value=l),options:V.value,searchable:!1},null,8,["modelValue","options"]),e("button",{class:"nav-lang-btn",onClick:j},c(n(w)==="zh"?"EN":"中"),1)])])])}}}),ee=J(q,[["__scopeId","data-v-7368f836"]]);export{ee as M};
@@ -0,0 +1 @@
1
+ .mgmt-nav[data-v-7368f836]{position:sticky;top:0;z-index:200;background:var(--glass-bg);border-bottom:1px solid var(--glass-border);flex-shrink:0}.mgmt-nav-inner[data-v-7368f836]{max-width:1160px;margin:0 auto;display:flex;align-items:center;gap:14px;padding:10px 16px;height:56px}.nav-back[data-v-7368f836]{display:flex;align-items:center;justify-content:center;width:36px;height:36px;background:var(--glass-highlight);border:1px solid var(--glass-border);border-radius:10px;color:var(--text-secondary);cursor:pointer;flex-shrink:0;transition:all var(--transition-duration)}.nav-back[data-v-7368f836]:hover{background:var(--accent-dim);border-color:var(--accent);color:var(--accent);transform:scale(1.05)}.nav-back[data-v-7368f836]:active{transform:scale(.95)}.nav-tabs[data-v-7368f836]{display:flex;gap:2px;flex:1;background:var(--glass-highlight);border-radius:12px;padding:4px;border:1px solid var(--glass-border)}.nav-tab[data-v-7368f836]{display:flex;align-items:center;gap:7px;padding:7px 16px;border-radius:10px;font-size:13px;font-weight:600;color:var(--text-secondary);text-decoration:none;transition:all .25s cubic-bezier(.4,0,.2,1);position:relative}.nav-tab[data-v-7368f836]:hover{background:var(--surface-bg);color:var(--text-primary)}.nav-tab.active[data-v-7368f836]{background:var(--accent);color:#fff;box-shadow:0 2px 12px #0003}.nav-tab svg[data-v-7368f836]{flex-shrink:0}.nav-controls[data-v-7368f836]{display:flex;align-items:center;gap:8px}.nav-select[data-v-7368f836]{padding:6px 10px;border:1px solid var(--glass-border);border-radius:8px;background:var(--glass-highlight);color:var(--text-primary);font-size:12px;cursor:pointer;font-family:inherit;font-weight:500;transition:border-color var(--transition-duration),box-shadow var(--transition-duration)}.nav-select[data-v-7368f836]:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-dim)}.nav-lang-btn[data-v-7368f836]{background:var(--glass-highlight);border:1px solid var(--glass-border);color:var(--text-primary);padding:6px 12px;border-radius:8px;cursor:pointer;font-size:12px;font-weight:600;font-family:inherit;min-width:36px;text-align:center;transition:all var(--transition-duration)}.nav-lang-btn[data-v-7368f836]:hover{background:var(--accent-dim);border-color:var(--accent);color:var(--accent);transform:scale(1.05)}.nav-lang-btn[data-v-7368f836]:active{transform:scale(.95)}@media(max-width:768px){.mgmt-nav-inner[data-v-7368f836]{padding:0 12px;gap:8px}.nav-tab[data-v-7368f836]{padding:7px 10px;font-size:12px;gap:4px}.nav-tab svg[data-v-7368f836]{width:13px;height:13px}}@media(max-width:480px){.nav-tab span[data-v-7368f836]{display:none}.nav-tab[data-v-7368f836]{padding:7px 12px;gap:0}.nav-tab svg[data-v-7368f836]{width:16px;height:16px;margin:0 auto}.nav-tabs[data-v-7368f836]{justify-content:center}}
@@ -0,0 +1 @@
1
+ import{n as I,Q as K,O as L,y as O,z as i,g as m,m as x,d as e,I as a,J as s,Z as c,W as g,$ as Q,F as C,E as M,S as q,e as J,a0 as W,V as Z,f as j,T as G,A as u,c as H,t as R,L as X,i as Y,p as ee,_ as le}from"./index-D8sObRrZ.js";import{M as te}from"./ManagementNav-B9XyX5Un.js";const ae={class:"mgmt-page"},se={class:"mgmt-page-content"},oe={class:"mgmt-header"},ne={class:"mgmt-toolbar"},de=["placeholder"],ie={key:0,class:"table-wrap"},ue={class:"mgmt-table"},re={key:1,class:"mgmt-empty"},me={key:2},ce={class:"table-wrap"},_e={class:"mgmt-table"},ve=["title"],ge=["title"],pe=["title"],he={class:"cell-actions"},be=["onClick"],fe=["onClick"],ye={class:"mgmt-pagination"},ke=["disabled"],we={class:"mgmt-page-info"},Ce=["disabled"],Me={class:"dialog dialog-wide"},Ve={class:"form-group"},Pe=["placeholder","disabled"],Se={class:"form-group"},$e=["value"],Ue={class:"form-group"},xe=["placeholder"],Te={class:"form-group"},ze=["placeholder"],Fe={class:"form-group"},Be=["placeholder"],Ne={class:"form-group"},Ae={class:"dialog-actions"},De=["disabled"],Ee=I({__name:"ProviderManagementPage",setup(Ie){const{t}=K(),h=L(),T=["openai","anthropic"],k=u([]),w=u(!1),b=u(!1),_=u(!1),p=u(!1),n=u({name:"",provider_type:"openai",api_key:"",base_url:"",default_model:"",description:""}),f=u(""),r=u(1),y=u(15),V=u(0),z=[{value:10,label:"10"},{value:15,label:"15"},{value:20,label:"20"},{value:50,label:"50"}],P=H(()=>Math.max(1,Math.ceil(V.value/y.value)));async function v(){w.value=!0;try{const d=await R({q:f.value,page:r.value,page_size:y.value});k.value=d.items,V.value=d.total}catch(d){h.show("Failed to load providers: "+d.message)}finally{w.value=!1}}function S(){r.value=1,v()}function $(d){r.value=d,v()}function F(){r.value=1,v()}function B(){p.value=!1,n.value={name:"",provider_type:"openai",api_key:"",base_url:"",default_model:"",description:""},_.value=!0}function N(d){p.value=!0,n.value={...d},_.value=!0}async function A(){if(n.value.name){b.value=!0;try{p.value&&n.value.name?await X(n.value.name,n.value):await Y(n.value),_.value=!1,await v()}catch(d){h.show("Failed to save: "+d.message)}finally{b.value=!1}}}async function D(d){if(await h.confirm(t("mgmt_confirm_delete")))try{await ee(d),await v()}catch(o){h.show("Failed to delete: "+o.message)}}function U(d,o){if(!d)return"-";const l=d.replace("T"," ").substring(0,16);return o?`${l} by ${o}`:l}return O(v),(d,o)=>(i(),m("div",ae,[x(te),e("div",se,[e("header",oe,[e("h1",null,a(s(t)("mgmt_providers")),1),e("button",{class:"btn-primary",onClick:B},a(s(t)("mgmt_new_provider")),1)]),e("div",ne,[c(e("input",{"onUpdate:modelValue":o[0]||(o[0]=l=>f.value=l),class:"mgmt-search",placeholder:s(t)("mgmt_search_placeholder"),onKeyup:Q(S,["enter"])},null,40,de),[[g,f.value]]),e("button",{class:"btn-search",onClick:S},a(s(t)("mgmt_search")),1)]),w.value?(i(),m("div",ie,[e("table",ue,[e("thead",null,[e("tr",null,[e("th",null,a(s(t)("mgmt_name")),1),e("th",null,a(s(t)("mgmt_provider_type")),1),e("th",null,a(s(t)("mgmt_base_url")),1),e("th",null,a(s(t)("mgmt_default_model")),1),e("th",null,a(s(t)("mgmt_created_at")),1),e("th",null,a(s(t)("mgmt_actions")),1)])]),e("tbody",null,[(i(),m(C,null,M(5,l=>e("tr",{key:l,class:"mgmt-skeleton-row"},[...o[12]||(o[12]=[e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"60%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"40%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"40%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell mgmt-skeleton-cell-sm",style:{width:"65%"}})],-1),e("td",null,[e("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1)])])),64))])])])):k.value.length===0?(i(),m("div",re,a(f.value?s(t)("mgmt_no_results"):s(t)("mgmt_no_providers")),1)):(i(),m("div",me,[e("div",ce,[e("table",_e,[e("thead",null,[e("tr",null,[e("th",null,a(s(t)("mgmt_name")),1),e("th",null,a(s(t)("mgmt_provider_type")),1),e("th",null,a(s(t)("mgmt_base_url")),1),e("th",null,a(s(t)("mgmt_default_model")),1),e("th",null,a(s(t)("mgmt_created_at")),1),e("th",null,a(s(t)("mgmt_actions")),1)])]),e("tbody",null,[(i(!0),m(C,null,M(k.value,l=>(i(),m("tr",{key:l.name},[e("td",{title:l.name},[e("code",null,a(l.name),1)],8,ve),e("td",null,[e("code",null,a(l.provider_type),1)]),e("td",null,[e("code",{class:"cell-url",title:l.base_url||"-"},a(l.base_url||"-"),9,ge)]),e("td",null,a(l.default_model||"-"),1),e("td",{class:"cell-audit",title:U(l.created_at,l.created_by)},a(U(l.created_at,l.created_by)),9,pe),e("td",he,[e("button",{class:"btn-action",onClick:E=>N(l)},a(s(t)("mgmt_edit")),9,be),e("button",{class:"btn-action btn-danger",onClick:E=>D(l.name)},a(s(t)("mgmt_delete")),9,fe)])]))),128))])])]),e("div",ye,[e("button",{class:"btn-page",disabled:r.value<=1,onClick:o[1]||(o[1]=l=>$(r.value-1))},a(s(t)("mgmt_prev_page")),9,ke),e("span",we,a(r.value)+" / "+a(P.value),1),e("button",{class:"btn-page",disabled:r.value>=P.value,onClick:o[2]||(o[2]=l=>$(r.value+1))},a(s(t)("mgmt_next_page")),9,Ce),x(q,{modelValue:y.value,"onUpdate:modelValue":o[3]||(o[3]=l=>y.value=l),modelModifiers:{number:!0},options:z,searchable:!1,onChange:F},null,8,["modelValue"])])]))]),(i(),J(G,{to:"body"},[_.value?(i(),m("div",{key:0,class:"dialog-overlay",onClick:o[11]||(o[11]=W(l=>_.value=!1,["self"]))},[e("div",Me,[e("h2",null,a(p.value?s(t)("mgmt_edit_provider"):s(t)("mgmt_new_provider_title")),1),e("div",Ve,[e("label",null,a(s(t)("mgmt_name")),1),c(e("input",{"onUpdate:modelValue":o[4]||(o[4]=l=>n.value.name=l),placeholder:s(t)("mgmt_placeholder_name"),disabled:p.value},null,8,Pe),[[g,n.value.name]])]),e("div",Se,[e("label",null,a(s(t)("mgmt_provider_type")),1),c(e("select",{"onUpdate:modelValue":o[5]||(o[5]=l=>n.value.provider_type=l),class:"mgmt-dialog-select"},[(i(),m(C,null,M(T,l=>e("option",{key:l,value:l},a(l),9,$e)),64))],512),[[Z,n.value.provider_type]])]),e("div",Ue,[e("label",null,a(s(t)("mgmt_api_key")),1),c(e("input",{"onUpdate:modelValue":o[6]||(o[6]=l=>n.value.api_key=l),type:"password",placeholder:s(t)("mgmt_api_key_placeholder")},null,8,xe),[[g,n.value.api_key]])]),e("div",Te,[e("label",null,a(s(t)("mgmt_base_url")),1),c(e("input",{"onUpdate:modelValue":o[7]||(o[7]=l=>n.value.base_url=l),placeholder:s(t)("mgmt_base_url_placeholder")},null,8,ze),[[g,n.value.base_url]])]),e("div",Fe,[e("label",null,a(s(t)("mgmt_default_model")),1),c(e("input",{"onUpdate:modelValue":o[8]||(o[8]=l=>n.value.default_model=l),placeholder:s(t)("mgmt_model_placeholder")},null,8,Be),[[g,n.value.default_model]])]),e("div",Ne,[e("label",null,a(s(t)("mgmt_description")),1),c(e("textarea",{"onUpdate:modelValue":o[9]||(o[9]=l=>n.value.description=l),rows:"2"},null,512),[[g,n.value.description]])]),e("div",Ae,[e("button",{class:"btn-action",onClick:o[10]||(o[10]=l=>_.value=!1)},a(s(t)("mgmt_cancel")),1),e("button",{class:"btn-primary",onClick:A,disabled:!n.value.name||b.value},a(b.value?s(t)("mgmt_saving"):s(t)("mgmt_save")),9,De)])])])):j("",!0)]))]))}}),Oe=le(Ee,[["__scopeId","data-v-6a84bb90"]]);export{Oe as default};
@@ -0,0 +1 @@
1
+ .mgmt-dialog-select[data-v-6a84bb90]{display:block;width:100%;padding:10px 14px;border:1px solid var(--glass-border);border-radius:10px;background:var(--glass-highlight);color:var(--text-primary);font-size:14px;font-family:inherit;outline:none;box-sizing:border-box;transition:border-color var(--transition-duration),box-shadow var(--transition-duration)}.mgmt-dialog-select[data-v-6a84bb90]:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-dim)}
@@ -0,0 +1 @@
1
+ .detail-header-actions[data-v-a0db1094]{display:flex;align-items:center;gap:8px}.btn-chat[data-v-a0db1094]{background:var(--accent-dim);color:var(--accent);border-color:var(--accent)}.btn-chat[data-v-a0db1094]:hover{background:var(--accent);color:#fff}.detail-icon[data-v-a0db1094]{font-size:28px;line-height:1}.detail-meta-row[data-v-a0db1094]{display:flex;align-items:center;gap:10px;margin-bottom:8px}.detail-id[data-v-a0db1094]{font-size:12px;font-family:var(--font-mono);opacity:.7}.agent-card-info[data-v-a0db1094]{display:flex;align-items:center;gap:8px;flex:1;min-width:0}