mh-local 0.1.0__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.
- mh_local-0.1.0/PKG-INFO +78 -0
- mh_local-0.1.0/README.md +69 -0
- mh_local-0.1.0/pyproject.toml +30 -0
- mh_local-0.1.0/src/mh_local/__init__.py +0 -0
- mh_local-0.1.0/src/mh_local/__main__.py +3 -0
- mh_local-0.1.0/src/mh_local/app.py +216 -0
- mh_local-0.1.0/src/mh_local/auth.py +37 -0
- mh_local-0.1.0/src/mh_local/main.py +85 -0
- mh_local-0.1.0/src/mh_local/static/assets/AgentManagementPage-DUOZSqNs.js +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/AgentManagementPage-KqRVCS47.css +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/ManagementNav-B04kM4lZ.css +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/ManagementNav-DXmOEWXa.js +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/ProviderManagementPage-8bXp0e9V.js +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/ProviderManagementPage-hhRPoUOy.css +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/SceneManagementPage-By_fC3Kg.js +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/SceneManagementPage-ChRTKhoK.css +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/ToolManagementPage-CNuLgZ0K.css +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/ToolManagementPage-CjRq41m3.js +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/index-BGUaSXe5.css +1 -0
- mh_local-0.1.0/src/mh_local/static/assets/index-DVOv8YZP.js +115 -0
- mh_local-0.1.0/src/mh_local/static/component/mh-extra-components.umd.js +9 -0
- mh_local-0.1.0/src/mh_local/static/component/mh-tool-components.umd.js +1 -0
- mh_local-0.1.0/src/mh_local/static/favicon.svg +3 -0
- mh_local-0.1.0/src/mh_local/static/index.html +23 -0
- mh_local-0.1.0/src/mh_local/storage/__init__.py +0 -0
- mh_local-0.1.0/src/mh_local/storage/paths.py +64 -0
- mh_local-0.1.0/src/mh_local/storage/provider_store.py +120 -0
- mh_local-0.1.0/src/mh_local/storage/registry_store.py +900 -0
- mh_local-0.1.0/src/mh_local/storage/script_store.py +40 -0
- mh_local-0.1.0/src/mh_local/storage/session_store.py +306 -0
mh_local-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: mh-local
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Minimal Harness Client — pure local Agent assistant
|
|
5
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
6
|
+
Requires-Dist: mh-gateway>=0.1.0
|
|
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.0**
|
|
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
|
+
- **Custom tool scripts** — drag-and-drop upload `.py` files; the gateway validates required metadata and the shebang interpreter, then runs `execute()` in a child process with streamed `ToolProgress` events
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv add mh-local
|
|
28
|
+
uv run mh-local
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Your browser will open automatically to `http://127.0.0.1:<port>`.
|
|
32
|
+
|
|
33
|
+
On first run, you'll be prompted to configure an LLM provider (API key, base URL) via the web UI's Provider management page.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
After starting, mh-local serves a full-featured chat UI at the local URL with:
|
|
38
|
+
|
|
39
|
+
- **Chat** — converse with AI agents with streaming responses
|
|
40
|
+
- **Manage Agents** — view and configure built-in agents (General Assistant `triage`, Tool Maker `tool-maker`)
|
|
41
|
+
- **Manage Tools** — inspect built-in tools (`bash`, `handoff`, `discover_agents`, `local_file_operator`) or upload custom tool scripts
|
|
42
|
+
- **Manage Scenes** — workspace management for different projects (one auto-created per `cwd` on first launch)
|
|
43
|
+
- **Manage Providers** — configure LLM providers (OpenAI, Anthropic, etc.)
|
|
44
|
+
|
|
45
|
+
## How it works
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
mh-local
|
|
49
|
+
├── FastAPI app (mh_gateway-based) — serves API + static files
|
|
50
|
+
├── Vue 3 SPA — chat UI served at /
|
|
51
|
+
├── File-based stores:
|
|
52
|
+
│ ├── sessions/ — conversation history
|
|
53
|
+
│ ├── providers.json — LLM provider config
|
|
54
|
+
│ └── registry.json — agents, tools, scenes
|
|
55
|
+
└── Local auth — auto-grants admin access
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
All configuration is done through the web UI. Persistent data lives at:
|
|
61
|
+
|
|
62
|
+
| Path | Contents |
|
|
63
|
+
|------|----------|
|
|
64
|
+
| `~/.config/mh-local/providers.json` | LLM provider credentials |
|
|
65
|
+
| `~/.config/mh-local/registry.json` | Agent/tool/scene registrations |
|
|
66
|
+
| `~/.config/mh-local/sessions/` | Chat session files |
|
|
67
|
+
| `~/.config/mh-local/logs/` | Runtime logs |
|
|
68
|
+
|
|
69
|
+
## Development
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Build frontend (SPA + component UMD bundles)
|
|
73
|
+
powershell scripts/build-mh-local-frontend.ps1 # Windows
|
|
74
|
+
bash scripts/build-mh-local-frontend.sh # Linux/macOS
|
|
75
|
+
|
|
76
|
+
# Run
|
|
77
|
+
uv run mh-local
|
|
78
|
+
```
|
mh_local-0.1.0/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
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.0**
|
|
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
|
+
- **Custom tool scripts** — drag-and-drop upload `.py` files; the gateway validates required metadata and the shebang interpreter, then runs `execute()` in a child process with streamed `ToolProgress` events
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv add mh-local
|
|
19
|
+
uv run mh-local
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Your browser will open automatically to `http://127.0.0.1:<port>`.
|
|
23
|
+
|
|
24
|
+
On first run, you'll be prompted to configure an LLM provider (API key, base URL) via the web UI's Provider management page.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
After starting, mh-local serves a full-featured chat UI at the local URL with:
|
|
29
|
+
|
|
30
|
+
- **Chat** — converse with AI agents with streaming responses
|
|
31
|
+
- **Manage Agents** — view and configure built-in agents (General Assistant `triage`, Tool Maker `tool-maker`)
|
|
32
|
+
- **Manage Tools** — inspect built-in tools (`bash`, `handoff`, `discover_agents`, `local_file_operator`) or upload custom tool scripts
|
|
33
|
+
- **Manage Scenes** — workspace management for different projects (one auto-created per `cwd` on first launch)
|
|
34
|
+
- **Manage Providers** — configure LLM providers (OpenAI, Anthropic, etc.)
|
|
35
|
+
|
|
36
|
+
## How it works
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
mh-local
|
|
40
|
+
├── FastAPI app (mh_gateway-based) — serves API + static files
|
|
41
|
+
├── Vue 3 SPA — chat UI served at /
|
|
42
|
+
├── File-based stores:
|
|
43
|
+
│ ├── sessions/ — conversation history
|
|
44
|
+
│ ├── providers.json — LLM provider config
|
|
45
|
+
│ └── registry.json — agents, tools, scenes
|
|
46
|
+
└── Local auth — auto-grants admin access
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
All configuration is done through the web UI. Persistent data lives at:
|
|
52
|
+
|
|
53
|
+
| Path | Contents |
|
|
54
|
+
|------|----------|
|
|
55
|
+
| `~/.config/mh-local/providers.json` | LLM provider credentials |
|
|
56
|
+
| `~/.config/mh-local/registry.json` | Agent/tool/scene registrations |
|
|
57
|
+
| `~/.config/mh-local/sessions/` | Chat session files |
|
|
58
|
+
| `~/.config/mh-local/logs/` | Runtime logs |
|
|
59
|
+
|
|
60
|
+
## Development
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Build frontend (SPA + component UMD bundles)
|
|
64
|
+
powershell scripts/build-mh-local-frontend.ps1 # Windows
|
|
65
|
+
bash scripts/build-mh-local-frontend.sh # Linux/macOS
|
|
66
|
+
|
|
67
|
+
# Run
|
|
68
|
+
uv run mh-local
|
|
69
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mh-local"
|
|
3
|
+
version = "0.1.0"
|
|
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.0",
|
|
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,216 @@
|
|
|
1
|
+
"""Local FastAPI app for ``mh-local``.
|
|
2
|
+
|
|
3
|
+
The app is wired through a single
|
|
4
|
+
:class:`mh_gateway.app.AdapterLifespan` that returns the gateway's
|
|
5
|
+
:class:`GatewayAdapters` bundle. All persistence is local-file
|
|
6
|
+
based.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from collections.abc import AsyncIterator
|
|
13
|
+
from contextlib import asynccontextmanager
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from fastapi import FastAPI
|
|
18
|
+
from fastapi.responses import FileResponse
|
|
19
|
+
from fastapi.staticfiles import StaticFiles
|
|
20
|
+
from minimal_harness.llm.factory import register_builtin_providers
|
|
21
|
+
from minimal_harness.llm.llm import ProviderFactory
|
|
22
|
+
|
|
23
|
+
from mh_gateway.adapters import (
|
|
24
|
+
AuthorizationProvider,
|
|
25
|
+
M2MAuthenticator,
|
|
26
|
+
MetadataRepository,
|
|
27
|
+
OutboundAuthProvider,
|
|
28
|
+
SessionRepository,
|
|
29
|
+
ToolScriptStore,
|
|
30
|
+
UserAuthenticator,
|
|
31
|
+
)
|
|
32
|
+
from mh_gateway.app import AdapterLifespan, GatewayAdapters, create_app
|
|
33
|
+
from mh_gateway.config import ConfigSchema
|
|
34
|
+
from mh_gateway.llm import DefaultLLMProviderService
|
|
35
|
+
|
|
36
|
+
from mh_local.auth import LocalAuthProvider
|
|
37
|
+
from mh_local.storage.provider_store import FileLLMConfigBackend
|
|
38
|
+
from mh_local.storage.registry_store import FileMetadataRepository
|
|
39
|
+
from mh_local.storage.session_store import JSONLSessionStore
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger("mh_local")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class _M2MAuth(M2MAuthenticator):
|
|
45
|
+
async def authenticate(self, request: Any) -> str | None:
|
|
46
|
+
return "local"
|
|
47
|
+
|
|
48
|
+
async def get_identity_headers(
|
|
49
|
+
self, request: Any, identity: str
|
|
50
|
+
) -> dict[str, str]:
|
|
51
|
+
return {}
|
|
52
|
+
|
|
53
|
+
async def close(self) -> None:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class _OutboundAuth(OutboundAuthProvider):
|
|
58
|
+
async def get_headers(
|
|
59
|
+
self, request: Any, target_url: str, target_type: str
|
|
60
|
+
) -> dict[str, str]:
|
|
61
|
+
return {}
|
|
62
|
+
|
|
63
|
+
async def close(self) -> None:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _build_settings() -> ConfigSchema:
|
|
68
|
+
return ConfigSchema(
|
|
69
|
+
dev_mode=False,
|
|
70
|
+
log_level="INFO",
|
|
71
|
+
db_path="",
|
|
72
|
+
db_auto_schema=False,
|
|
73
|
+
enable_eval=False,
|
|
74
|
+
metrics_enabled=False,
|
|
75
|
+
cors_origins=[],
|
|
76
|
+
eval_results_dir="",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_adapter_lifespan(
|
|
81
|
+
*,
|
|
82
|
+
user_auth: UserAuthenticator | None = None,
|
|
83
|
+
authorization: AuthorizationProvider | None = None,
|
|
84
|
+
metadata: MetadataRepository | None = None,
|
|
85
|
+
sessions: SessionRepository | None = None,
|
|
86
|
+
llm_provider: Any = None,
|
|
87
|
+
m2m_auth: M2MAuthenticator | None = None,
|
|
88
|
+
outbound_auth: OutboundAuthProvider | None = None,
|
|
89
|
+
tool_script_store: ToolScriptStore | None = None,
|
|
90
|
+
) -> AdapterLifespan:
|
|
91
|
+
"""Construct the default :class:`AdapterLifespan` for ``mh-local``."""
|
|
92
|
+
|
|
93
|
+
settings = _build_settings()
|
|
94
|
+
factory = ProviderFactory()
|
|
95
|
+
register_builtin_providers(factory)
|
|
96
|
+
backend = FileLLMConfigBackend()
|
|
97
|
+
default_llm = llm_provider or DefaultLLMProviderService.from_components(
|
|
98
|
+
factory=factory, backend=backend
|
|
99
|
+
)
|
|
100
|
+
default_user_auth = user_auth or LocalAuthProvider()
|
|
101
|
+
default_authorization = authorization or default_user_auth
|
|
102
|
+
|
|
103
|
+
@asynccontextmanager
|
|
104
|
+
async def _lifespan(app: FastAPI) -> AsyncIterator[GatewayAdapters]:
|
|
105
|
+
metadata_repo = metadata or FileMetadataRepository()
|
|
106
|
+
await metadata_repo.sync_cwd_scene()
|
|
107
|
+
sessions_repo = sessions or JSONLSessionStore()
|
|
108
|
+
|
|
109
|
+
script_store_impl = tool_script_store
|
|
110
|
+
if script_store_impl is None:
|
|
111
|
+
from mh_local.storage.script_store import LocalScriptStore
|
|
112
|
+
|
|
113
|
+
script_store_impl = LocalScriptStore()
|
|
114
|
+
|
|
115
|
+
yield GatewayAdapters(
|
|
116
|
+
settings=settings,
|
|
117
|
+
user_auth=default_user_auth,
|
|
118
|
+
authorization=default_authorization,
|
|
119
|
+
m2m_auth=m2m_auth or _M2MAuth(),
|
|
120
|
+
outbound_auth=outbound_auth or _OutboundAuth(),
|
|
121
|
+
metadata=metadata_repo,
|
|
122
|
+
llm=default_llm,
|
|
123
|
+
sessions=sessions_repo,
|
|
124
|
+
eval_results=None,
|
|
125
|
+
tool_script_store=script_store_impl,
|
|
126
|
+
)
|
|
127
|
+
await metadata_repo.close()
|
|
128
|
+
await sessions_repo.close()
|
|
129
|
+
await default_llm.close()
|
|
130
|
+
await script_store_impl.close()
|
|
131
|
+
|
|
132
|
+
return _lifespan
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def create_local_app() -> FastAPI:
|
|
136
|
+
app = create_app(
|
|
137
|
+
settings=_build_settings(),
|
|
138
|
+
adapters=build_adapter_lifespan(),
|
|
139
|
+
)
|
|
140
|
+
_mount_mh_local_static(app)
|
|
141
|
+
return app
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _mount_mh_local_static(app: FastAPI) -> None:
|
|
145
|
+
static_dir = Path(__file__).resolve().parent / "static"
|
|
146
|
+
if not static_dir.is_dir():
|
|
147
|
+
logger.warning("MH-Local static directory not found at %s", static_dir)
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
subdirs = [
|
|
151
|
+
("/assets", static_dir / "assets"),
|
|
152
|
+
("/component", static_dir / "component"),
|
|
153
|
+
]
|
|
154
|
+
for url_path, fs_path in subdirs:
|
|
155
|
+
if fs_path.is_dir():
|
|
156
|
+
app.mount(
|
|
157
|
+
url_path,
|
|
158
|
+
StaticFiles(directory=str(fs_path)),
|
|
159
|
+
name=f"mh_local_{url_path.lstrip('/')}",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
favicon = static_dir / "favicon.svg"
|
|
163
|
+
if favicon.is_file():
|
|
164
|
+
|
|
165
|
+
@app.get("/favicon.svg", include_in_schema=False)
|
|
166
|
+
async def _favicon():
|
|
167
|
+
return FileResponse(str(favicon))
|
|
168
|
+
|
|
169
|
+
@app.middleware("http")
|
|
170
|
+
async def _mh_local_spa_fallback(request: Any, call_next: Any) -> Any:
|
|
171
|
+
response = await call_next(request)
|
|
172
|
+
if (
|
|
173
|
+
response.status_code == 404
|
|
174
|
+
and request.method == "GET"
|
|
175
|
+
and request.url.path != "/api"
|
|
176
|
+
and not request.url.path.startswith("/api/")
|
|
177
|
+
and not request.url.path.startswith("/docs")
|
|
178
|
+
and not request.url.path.startswith("/openapi")
|
|
179
|
+
and not request.url.path.startswith("/redoc")
|
|
180
|
+
and not request.url.path.startswith("/assets")
|
|
181
|
+
and not request.url.path.startswith("/component")
|
|
182
|
+
):
|
|
183
|
+
index_path = static_dir / "index.html"
|
|
184
|
+
if index_path.is_file():
|
|
185
|
+
return FileResponse(str(index_path))
|
|
186
|
+
return response
|
|
187
|
+
|
|
188
|
+
_mount_component_sources(app)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _mount_component_sources(app: FastAPI) -> None:
|
|
192
|
+
static_dir = Path(__file__).resolve().parent / "static"
|
|
193
|
+
components = static_dir / "component"
|
|
194
|
+
if not components.is_dir():
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
from fastapi import APIRouter
|
|
198
|
+
|
|
199
|
+
router = APIRouter(prefix="/api/v1/component-sources", tags=["component-sources"])
|
|
200
|
+
|
|
201
|
+
sources = []
|
|
202
|
+
for f in components.iterdir():
|
|
203
|
+
if f.suffix == ".js":
|
|
204
|
+
sources.append(
|
|
205
|
+
{
|
|
206
|
+
"id": f.stem,
|
|
207
|
+
"label": f.stem.replace("mh-", "").replace("-", " ").title(),
|
|
208
|
+
"url": f"/component/{f.name}",
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
@router.get("")
|
|
213
|
+
async def get_component_sources():
|
|
214
|
+
return sources
|
|
215
|
+
|
|
216
|
+
app.include_router(router)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from mh_gateway.adapters import (
|
|
6
|
+
AuthorizationProvider,
|
|
7
|
+
UserAuthenticator,
|
|
8
|
+
UserIdentity,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LocalAuthProvider(UserAuthenticator, AuthorizationProvider):
|
|
13
|
+
"""Single-user auth provider used by ``mh-local``.
|
|
14
|
+
|
|
15
|
+
Always resolves to the local admin identity. Production
|
|
16
|
+
deployments that need real authentication should swap this
|
|
17
|
+
for a custom :class:`UserAuthenticator` /
|
|
18
|
+
:class:`AuthorizationProvider` pair.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
async def verify(self, request: Any) -> UserIdentity:
|
|
22
|
+
return UserIdentity(
|
|
23
|
+
user_id="local",
|
|
24
|
+
username="Local User",
|
|
25
|
+
roles=["admin"],
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
async def logout(self, request: Any, response: Any) -> None:
|
|
29
|
+
response.set_cookie(
|
|
30
|
+
key="x-user-id", value="", httponly=True, max_age=0, path="/"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
async def get_permissions(self, user_id: str) -> list[str]:
|
|
34
|
+
return ["*", "*:*:*"]
|
|
35
|
+
|
|
36
|
+
async def check(self, user_id: str, permission: str) -> bool:
|
|
37
|
+
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
|
+
import{n as $e,U as Me,Q as Oe,$ as se,z as Te,A as m,g as v,m as N,d as l,J as n,K as a,a1 as p,Z as g,a2 as Ne,F as A,G as X,S as D,e as ne,a3 as ie,X as Ae,l as y,f as I,T as ce,y as Fe,c as M,C as _,s as Pe,u as Je,t as Le,L as Be,h as We,o as De,_ as Ie}from"./index-DVOv8YZP.js";import{M as Ke}from"./ManagementNav-DXmOEWXa.js";const je={class:"mgmt-page"},Ee={class:"mgmt-page-content"},Qe={class:"mgmt-header"},qe={class:"mgmt-toolbar"},Ge=["placeholder"],Xe={key:0,class:"table-wrap"},Ze={class:"mgmt-table"},He={key:1,class:"mgmt-empty"},Re={key:2},Ye={class:"table-wrap"},el={class:"mgmt-table"},ll=["title"],tl=["title"],ol=["title"],al=["title"],sl=["title"],nl={class:"cell-actions"},il=["onClick"],cl=["onClick"],dl={class:"mgmt-pagination"},ul=["disabled"],ml={class:"mgmt-page-info"},rl=["disabled"],pl={class:"dialog dialog-wide"},_l={class:"form-group"},vl=["disabled","placeholder"],gl={class:"form-group"},yl=["placeholder"],hl={class:"form-group"},fl={class:"tc-label-row"},bl=["title"],kl={class:"form-group"},zl={class:"tc-label-row"},wl=["title"],Vl={class:"form-group"},Cl={class:"form-group"},xl={class:"form-group"},Ul={key:0,class:"locale-section",open:""},Sl={key:0,class:"checkbox-label"},$l=["onUpdate:modelValue"],Ml={class:"tc-label-row"},Ol=["onClick","title"],Tl=["onUpdate:modelValue","placeholder"],Nl=["onUpdate:modelValue","min","max","step","placeholder"],Al=["onUpdate:modelValue","placeholder"],Fl={key:0,class:"locale-field"},Pl={class:"locale-field-label"},Jl={class:"locale-input-row"},Ll={class:"locale-lang"},Bl=["title"],Wl={class:"locale-lang"},Dl=["title"],Il={class:"locale-section"},Kl={class:"form-group"},jl={class:"tc-label-row"},El=["title"],Ql=["placeholder"],ql={class:"locale-section"},Gl={class:"locale-field"},Xl={class:"locale-field-label"},Zl={class:"locale-input-row"},Hl={class:"locale-lang"},Rl={class:"locale-lang"},Yl=["placeholder"],et={class:"locale-field"},lt={class:"locale-field-label"},tt={class:"locale-input-row"},ot={class:"locale-lang"},at=["title"],st={class:"locale-lang"},nt=["title"],it=["placeholder"],ct={class:"locale-field"},dt={class:"locale-field-label"},ut={class:"locale-input-row"},mt={class:"locale-lang"},rt=["title"],pt={class:"locale-lang"},_t=["title"],vt=["placeholder"],gt={class:"dialog-actions"},yt=["disabled"],ht={class:"tc-overlay-content"},ft={class:"tc-overlay-header"},bt={class:"tc-overlay-title"},kt={class:"tc-overlay-body"},zt=$e({__name:"AgentManagementPage",setup(wt){const{t:s,localeVal:C}=Me(),O=Oe(),F=_([]),de=M(()=>F.value.map(o=>({value:o.name,label:o.name}))),ue=M(()=>F.value.find(o=>o.name===i.value.provider)),Z=M(()=>{const o=ue.value;return!o||!o.models||o.models.length===0?[]:o.models.map(e=>({value:e.code||e.id,label:e.display_name||e.code||e.id}))}),K=_([]),j=_(!1),U=_(!1),T=_(!1),i=_({name:"",display_name:"",display_name_locale:"",description:"",description_locale:"",system_prompt:"",system_prompt_locale:"",provider:"openai",model:"",llm_config:{},agent_type:"simple"});se(()=>i.value.provider,()=>{const o=Z.value;o.length>0&&!o.some(e=>e.value===i.value.model)&&(i.value.model=o[0].value)});const c=_({display_zh:"",display_en:"",desc_zh:"",desc_en:"",prompt_zh:"",prompt_en:""});function E(o){if(!o)return{zh:"",en:""};try{const e=JSON.parse(o);return{zh:e.zh??"",en:e.en??""}}catch{return{zh:"",en:""}}}function Q(o,e){return!o&&!e?"":JSON.stringify({zh:o,en:e})}function me(){const o=E(i.value.display_name_locale);c.value.display_zh=o.zh,c.value.display_en=o.en;const e=E(i.value.description_locale);c.value.desc_zh=e.zh,c.value.desc_en=e.en;const d=E(i.value.system_prompt_locale);c.value.prompt_zh=d.zh,c.value.prompt_en=d.en}function re(){i.value.display_name_locale=Q(c.value.display_zh,c.value.display_en),i.value.description_locale=Q(c.value.desc_zh,c.value.desc_en),i.value.system_prompt_locale=Q(c.value.prompt_zh,c.value.prompt_en)}const P=_(""),w=_(1),J=_(15),H=_(0),pe=[{value:10,label:"10"},{value:15,label:"15"},{value:20,label:"20"},{value:50,label:"50"}],R=M(()=>Math.max(1,Math.ceil(H.value/J.value))),L=_(!1),x=_(""),S=_({});function _e(o){return C(JSON.stringify({zh:o.display_name_zh,en:o.display_name}),o.display_name)}function ve(o){return o.settings_title||o.settings_title_zh?C(JSON.stringify({zh:o.settings_title_zh,en:o.settings_title}),o.settings_title||""):""}function B(o){return C(JSON.stringify({zh:o.display_name_zh,en:o.display_name}),o.display_name)}function q(o){return!o.placeholder&&!o.placeholder_zh?"":C(JSON.stringify({zh:o.placeholder_zh,en:o.placeholder}),o.placeholder||"")}async function ge(){try{const o=await Pe(),e={};for(const d of o)e[d.value]=d;S.value=e}catch{S.value={}}}const u=_({}),h=_({zh:"",en:""});function ye(o){if(!o)return{zh:"",en:""};try{const e=JSON.parse(o);return{zh:e.zh??"",en:e.en??""}}catch{return{zh:"",en:""}}}function he(o,e){return!o&&!e?"":JSON.stringify({zh:o,en:e})}function fe(){const o=u.value.compaction_prompt_locale,e=ye(o);h.value.zh=e.zh,h.value.en=e.en}function be(){const o=he(h.value.zh,h.value.en);o?u.value.compaction_prompt_locale=o:delete u.value.compaction_prompt_locale}const V=M(()=>S.value[i.value.agent_type??"simple"]??S.value.simple??null),ke=M(()=>Object.values(S.value).map(o=>({value:o.value,label:_e(o)})));function Y(){const o=V.value;if(o&&o.settings_key&&o.settings_fields){const e=i.value[o.settings_key]??{},d={};for(const b of o.settings_fields)d[b.key]=e[b.key]??void 0;e.compaction_prompt_locale!==void 0&&(d.compaction_prompt_locale=e.compaction_prompt_locale),u.value=d,fe()}else u.value={}}function ze(){be();for(const e of Object.values(S.value))e.settings_key&&delete i.value[e.settings_key];const o=V.value;if(o&&o.settings_key&&o.settings_fields){const e={};for(const d of o.settings_fields){const b=u.value[d.key];b!=null&&b!==""&&(e[d.key]=d.type==="number"?Number(b):b)}u.value.compaction_prompt_locale&&(e.compaction_prompt_locale=u.value.compaction_prompt_locale),Object.keys(e).length>0&&(i.value[o.settings_key]=e)}}se(()=>i.value.agent_type,()=>{u.value={},h.value={zh:"",en:""},Y()});const G=_(!1),ee=_(""),f=_("");let r=null;function k(o,e,d){ee.value=o,f.value=e,r=d,G.value=!0}function le(){if(r==="description")i.value.description=f.value;else if(r==="system_prompt")i.value.system_prompt=f.value;else if(r==="llm_config")x.value=f.value;else if(r==="locale_desc_zh")c.value.desc_zh=f.value;else if(r==="locale_desc_en")c.value.desc_en=f.value;else if(r==="locale_prompt_zh")c.value.prompt_zh=f.value;else if(r==="locale_prompt_en")c.value.prompt_en=f.value;else if(r==="compaction_prompt_locale_zh")h.value.zh=f.value;else if(r==="compaction_prompt_locale_en")h.value.en=f.value;else if(r&&r.startsWith("settings:")){const o=r.slice(9);u.value[o]=f.value}G.value=!1,r=null}async function we(){try{const o=await Je({page_size:100});F.value=o.items}catch{F.value=[{name:"openai",provider_type:"openai"},{name:"anthropic",provider_type:"anthropic"}]}}async function $(){j.value=!0;try{const o=await Le({q:P.value,page:w.value,page_size:J.value});K.value=o.items,H.value=o.total}catch(o){O.show("Failed to load agents: "+o.message)}finally{j.value=!1}}function te(){w.value=1,$()}function oe(o){w.value=o,$()}function Ve(){w.value=1,$()}function Ce(){T.value=!1,i.value={name:"",display_name:"",display_name_locale:"",description:"",description_locale:"",system_prompt:"",system_prompt_locale:"",provider:"openai",model:"",llm_config:{},agent_type:"simple"},c.value={display_zh:"",display_en:"",desc_zh:"",desc_en:"",prompt_zh:"",prompt_en:""},x.value="",u.value={},h.value={zh:"",en:""},U.value=!0}function xe(o){T.value=!0,i.value={...o},x.value=o.llm_config?JSON.stringify(o.llm_config,null,2):"",me(),Y(),U.value=!0}async function Ue(){if(re(),ze(),x.value.trim())try{i.value.llm_config=JSON.parse(x.value)}catch{O.show("Invalid JSON in model config");return}else i.value.llm_config={};L.value=!0;try{T.value&&i.value.name?await Be(i.value.name,i.value):await We(i.value),U.value=!1,await $()}catch(o){O.show("Failed to save: "+o.message)}finally{L.value=!1}}function W(o,e){if(!o)return"-";const d=o.replace("T"," ").substring(0,16);return e?`${d} by ${e}`:d}async function Se(o){if(await O.confirm(s("alert_confirm_delete_agent",{name:o})))try{await De(o),await $()}catch(e){O.show("Failed to delete: "+e.message)}}return Te(()=>{ge(),we(),$()}),(o,e)=>{var d,b,ae;return m(),v("div",je,[N(Ke),l("div",Ee,[l("header",Qe,[l("h1",null,n(a(s)("mgmt_agents")),1),l("button",{class:"btn-primary",onClick:Ce},n(a(s)("mgmt_new_agent")),1)]),l("div",null,[l("div",qe,[p(l("input",{"onUpdate:modelValue":e[0]||(e[0]=t=>P.value=t),class:"mgmt-search",placeholder:a(s)("mgmt_search_placeholder"),onKeyup:Ne(te,["enter"])},null,40,Ge),[[g,P.value]]),l("button",{class:"btn-search",onClick:te},n(a(s)("mgmt_search")),1)]),j.value?(m(),v("div",Xe,[l("table",Ze,[l("thead",null,[l("tr",null,[l("th",null,n(a(s)("mgmt_name")),1),l("th",null,n(a(s)("mgmt_display_name")),1),l("th",null,n(a(s)("mgmt_description")),1),l("th",null,n(a(s)("mgmt_created_at")),1),l("th",null,n(a(s)("mgmt_updated_at")),1),l("th",null,n(a(s)("mgmt_actions")),1)])]),l("tbody",null,[(m(),v(A,null,X(5,t=>l("tr",{key:t,class:"mgmt-skeleton-row"},[...e[32]||(e[32]=[l("td",null,[l("div",{class:"mgmt-skeleton-cell",style:{width:"60%"}})],-1),l("td",null,[l("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1),l("td",null,[l("div",{class:"mgmt-skeleton-cell",style:{width:"70%"}})],-1),l("td",null,[l("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1),l("td",null,[l("div",{class:"mgmt-skeleton-cell mgmt-skeleton-cell-sm",style:{width:"65%"}})],-1),l("td",null,[l("div",{class:"mgmt-skeleton-cell mgmt-skeleton-cell-sm",style:{width:"65%"}})],-1),l("td",null,[l("div",{class:"mgmt-skeleton-cell",style:{width:"50%"}})],-1)])])),64))])])])):K.value.length===0?(m(),v("div",He,n(P.value?a(s)("mgmt_no_results"):a(s)("mgmt_no_agents")),1)):(m(),v("div",Re,[l("div",Ye,[l("table",el,[l("thead",null,[l("tr",null,[l("th",null,n(a(s)("mgmt_name")),1),l("th",null,n(a(s)("mgmt_display_name")),1),l("th",null,n(a(s)("mgmt_description")),1),l("th",null,n(a(s)("mgmt_created_at")),1),l("th",null,n(a(s)("mgmt_updated_at")),1),l("th",null,n(a(s)("mgmt_actions")),1)])]),l("tbody",null,[(m(!0),v(A,null,X(K.value,t=>(m(),v("tr",{key:t.name},[l("td",{title:t.name},[l("code",null,n(t.name),1)],8,ll),l("td",{title:a(C)(t.display_name_locale,t.display_name)},n(a(C)(t.display_name_locale,t.display_name)),9,tl),l("td",{class:"cell-desc",title:a(C)(t.description_locale,t.description)},n(a(C)(t.description_locale,t.description)),9,ol),l("td",{class:"cell-audit",title:W(t.created_at,t.created_by)},n(W(t.created_at,t.created_by)),9,al),l("td",{class:"cell-audit",title:W(t.updated_at,t.updated_by)},n(W(t.updated_at,t.updated_by)),9,sl),l("td",nl,[l("button",{class:"btn-action",onClick:z=>xe(t)},n(a(s)("mgmt_edit")),9,il),l("button",{class:"btn-action btn-danger",onClick:z=>Se(t.name)},n(a(s)("mgmt_delete")),9,cl)])]))),128))])])]),l("div",dl,[l("button",{class:"btn-page",disabled:w.value<=1,onClick:e[1]||(e[1]=t=>oe(w.value-1))},n(a(s)("mgmt_prev_page")),9,ul),l("span",ml,n(w.value)+" / "+n(R.value),1),l("button",{class:"btn-page",disabled:w.value>=R.value,onClick:e[2]||(e[2]=t=>oe(w.value+1))},n(a(s)("mgmt_next_page")),9,rl),N(D,{modelValue:J.value,"onUpdate:modelValue":e[3]||(e[3]=t=>J.value=t),modelModifiers:{number:!0},options:pe,searchable:!1,onChange:Ve},null,8,["modelValue"])])])),(m(),ne(ce,{to:"body"},[U.value?(m(),v("div",{key:0,class:"dialog-overlay",onMousedown:e[30]||(e[30]=ie(t=>U.value=!1,["self"]))},[l("div",pl,[l("h2",null,n(T.value?a(s)("mgmt_edit_agent"):a(s)("mgmt_new_agent_title")),1),l("div",_l,[l("label",null,n(a(s)("mgmt_name")),1),p(l("input",{"onUpdate:modelValue":e[4]||(e[4]=t=>i.value.name=t),disabled:T.value,placeholder:a(s)("mgmt_placeholder_id")},null,8,vl),[[g,i.value.name]])]),l("div",gl,[l("label",null,n(a(s)("mgmt_display_name")),1),p(l("input",{"onUpdate:modelValue":e[5]||(e[5]=t=>i.value.display_name=t),placeholder:a(s)("mgmt_placeholder_display_name")},null,8,yl),[[g,i.value.display_name]])]),l("div",hl,[l("div",fl,[l("label",null,n(a(s)("mgmt_description")),1),l("button",{class:"tc-fullscreen-btn",onClick:e[6]||(e[6]=t=>k(a(s)("mgmt_description"),i.value.description??"","description")),title:a(s)("mgmt_tc_source_code")},"⛶",8,bl)]),p(l("textarea",{"onUpdate:modelValue":e[7]||(e[7]=t=>i.value.description=t),rows:"2"},null,512),[[g,i.value.description]])]),l("div",kl,[l("div",zl,[l("label",null,n(a(s)("mgmt_system_prompt")),1),l("button",{class:"tc-fullscreen-btn",onClick:e[8]||(e[8]=t=>k(a(s)("mgmt_system_prompt"),i.value.system_prompt??"","system_prompt")),title:a(s)("mgmt_tc_source_code")},"⛶",8,wl)]),p(l("textarea",{"onUpdate:modelValue":e[9]||(e[9]=t=>i.value.system_prompt=t),rows:"6",class:"mono"},null,512),[[g,i.value.system_prompt]])]),l("div",Vl,[l("label",null,n(a(s)("mgmt_provider")),1),N(D,{modelValue:i.value.provider,"onUpdate:modelValue":e[10]||(e[10]=t=>i.value.provider=t),options:de.value,searchable:!1},null,8,["modelValue","options"])]),l("div",Cl,[l("label",null,n(a(s)("mgmt_model")),1),N(D,{modelValue:i.value.model,"onUpdate:modelValue":e[11]||(e[11]=t=>i.value.model=t),options:Z.value,searchable:!0,placeholder:a(s)("mgmt_model_placeholder")},null,8,["modelValue","options","placeholder"])]),l("div",xl,[l("label",null,n(a(s)("mgmt_agent_type")),1),N(D,{modelValue:i.value.agent_type,"onUpdate:modelValue":e[12]||(e[12]=t=>i.value.agent_type=t),options:ke.value,searchable:!1},null,8,["modelValue","options"])]),V.value&&V.value.settings_fields&&V.value.settings_fields.length>0?(m(),v("details",Ul,[l("summary",null,n(ve(V.value)),1),(m(!0),v(A,null,X(V.value.settings_fields,t=>(m(),v("div",{class:"form-group",key:t.key},[t.type==="boolean"?(m(),v("label",Sl,[p(l("input",{"onUpdate:modelValue":z=>u.value[t.key]=z,type:"checkbox"},null,8,$l),[[Ae,u.value[t.key]]]),y(" "+n(B(t)),1)])):t.type==="longtext"?(m(),v(A,{key:1},[l("div",Ml,[l("label",null,n(B(t)),1),l("button",{class:"tc-fullscreen-btn",onClick:z=>k(B(t),u.value[t.key]??"","settings:"+t.key),title:a(s)("mgmt_tc_source_code")},"⛶",8,Ol)]),p(l("textarea",{"onUpdate:modelValue":z=>u.value[t.key]=z,rows:"6",class:"mono",placeholder:q(t)},null,8,Tl),[[g,u.value[t.key]]])],64)):(m(),v(A,{key:2},[l("label",null,n(B(t)),1),t.type==="number"?p((m(),v("input",{key:0,"onUpdate:modelValue":z=>u.value[t.key]=z,type:"number",min:t.min,max:t.max,step:t.step??1,placeholder:q(t)},null,8,Nl)),[[g,u.value[t.key],void 0,{number:!0}]]):p((m(),v("input",{key:1,"onUpdate:modelValue":z=>u.value[t.key]=z,placeholder:q(t)},null,8,Al)),[[g,u.value[t.key]]])],64))]))),128)),V.value.settings_key==="compaction"||V.value.settings_key==="tool_compaction"?(m(),v("div",Fl,[l("div",Pl,n(a(s)("mgmt_compaction_prompt_locale")),1),l("div",Jl,[l("label",Ll,[e[33]||(e[33]=y("zh ",-1)),l("button",{class:"tc-fullscreen-btn",onClick:e[13]||(e[13]=t=>k("zh "+a(s)("mgmt_compaction_prompt_locale"),h.value.zh,"compaction_prompt_locale_zh")),title:a(s)("mgmt_tc_source_code")},"⛶",8,Bl),e[34]||(e[34]=y()),p(l("textarea",{"onUpdate:modelValue":e[14]||(e[14]=t=>h.value.zh=t),rows:"3",placeholder:"中文压缩提示词"},null,512),[[g,h.value.zh]])]),l("label",Wl,[e[35]||(e[35]=y("en ",-1)),l("button",{class:"tc-fullscreen-btn",onClick:e[15]||(e[15]=t=>k("en "+a(s)("mgmt_compaction_prompt_locale"),h.value.en,"compaction_prompt_locale_en")),title:a(s)("mgmt_tc_source_code")},"⛶",8,Dl),e[36]||(e[36]=y()),p(l("textarea",{"onUpdate:modelValue":e[16]||(e[16]=t=>h.value.en=t),rows:"3",placeholder:"Compaction prompt"},null,512),[[g,h.value.en]])])])])):I("",!0)])):I("",!0),l("details",Il,[l("summary",null,n(a(s)("mgmt_llm_config")),1),l("div",Kl,[l("div",jl,[e[37]||(e[37]=l("span",null,null,-1)),l("button",{class:"tc-fullscreen-btn",onClick:e[17]||(e[17]=t=>k(a(s)("mgmt_llm_config"),x.value,"llm_config")),title:a(s)("mgmt_tc_source_code")},"⛶",8,El)]),p(l("textarea",{"onUpdate:modelValue":e[18]||(e[18]=t=>x.value=t),rows:"4",class:"mono",placeholder:a(s)("mgmt_llm_config_placeholder")},null,8,Ql),[[g,x.value]])])]),l("details",ql,[l("summary",null,n(a(s)("mgmt_translations")),1),l("div",Gl,[l("div",Xl,n(a(s)("mgmt_locale_display")),1),l("div",Zl,[l("label",Hl,[e[38]||(e[38]=y("zh ",-1)),p(l("input",{"onUpdate:modelValue":e[19]||(e[19]=t=>c.value.display_zh=t),placeholder:"中文名称"},null,512),[[g,c.value.display_zh]])]),l("label",Rl,[e[39]||(e[39]=y("en ",-1)),p(l("input",{"onUpdate:modelValue":e[20]||(e[20]=t=>c.value.display_en=t),placeholder:a(s)("mgmt_placeholder_display_name")},null,8,Yl),[[g,c.value.display_en]])])])]),l("div",et,[l("div",lt,n(a(s)("mgmt_locale_desc")),1),l("div",tt,[l("label",ot,[e[40]||(e[40]=y("zh ",-1)),l("button",{class:"tc-fullscreen-btn",onClick:e[21]||(e[21]=t=>k("zh "+a(s)("mgmt_locale_desc"),c.value.desc_zh,"locale_desc_zh")),title:a(s)("mgmt_tc_source_code")},"⛶",8,at),e[41]||(e[41]=y()),p(l("textarea",{"onUpdate:modelValue":e[22]||(e[22]=t=>c.value.desc_zh=t),rows:"2",placeholder:"中文描述"},null,512),[[g,c.value.desc_zh]])]),l("label",st,[e[42]||(e[42]=y("en ",-1)),l("button",{class:"tc-fullscreen-btn",onClick:e[23]||(e[23]=t=>k("en "+a(s)("mgmt_locale_desc"),c.value.desc_en,"locale_desc_en")),title:a(s)("mgmt_tc_source_code")},"⛶",8,nt),e[43]||(e[43]=y()),p(l("textarea",{"onUpdate:modelValue":e[24]||(e[24]=t=>c.value.desc_en=t),rows:"2",placeholder:a(s)("mgmt_placeholder_desc")},null,8,it),[[g,c.value.desc_en]])])])]),l("div",ct,[l("div",dt,n(a(s)("mgmt_locale_prompt")),1),l("div",ut,[l("label",mt,[e[44]||(e[44]=y("zh ",-1)),l("button",{class:"tc-fullscreen-btn",onClick:e[25]||(e[25]=t=>k("zh "+a(s)("mgmt_locale_prompt"),c.value.prompt_zh,"locale_prompt_zh")),title:a(s)("mgmt_tc_source_code")},"⛶",8,rt),e[45]||(e[45]=y()),p(l("textarea",{"onUpdate:modelValue":e[26]||(e[26]=t=>c.value.prompt_zh=t),rows:"3",placeholder:"中文系统提示"},null,512),[[g,c.value.prompt_zh]])]),l("label",pt,[e[46]||(e[46]=y("en ",-1)),l("button",{class:"tc-fullscreen-btn",onClick:e[27]||(e[27]=t=>k("en "+a(s)("mgmt_locale_prompt"),c.value.prompt_en,"locale_prompt_en")),title:a(s)("mgmt_tc_source_code")},"⛶",8,_t),e[47]||(e[47]=y()),p(l("textarea",{"onUpdate:modelValue":e[28]||(e[28]=t=>c.value.prompt_en=t),rows:"3",placeholder:a(s)("mgmt_placeholder_prompt")},null,8,vt),[[g,c.value.prompt_en]])])])])]),l("div",gt,[l("button",{class:"btn-cancel",onClick:e[29]||(e[29]=t=>U.value=!1)},n(a(s)("mgmt_cancel")),1),l("button",{class:"btn-primary",disabled:L.value||!i.value.name,onClick:Ue},n(L.value?a(s)("mgmt_saving"):a(s)("mgmt_save")),9,yt)])])],32)):I("",!0)])),(m(),ne(ce,{to:"body"},[G.value?(m(),v("div",{key:0,class:"tc-overlay",onMousedown:ie(le,["self"])},[l("div",ht,[l("div",ft,[l("span",bt,n(ee.value),1),l("div",{class:"tc-overlay-header-actions"},[l("button",{class:"tc-fullscreen-close",onClick:le},"✕")])]),l("div",kt,[p(l("textarea",{"onUpdate:modelValue":e[31]||(e[31]=t=>f.value=t),class:Fe(a(r)==="system_prompt"||a(r)==="llm_config"||(d=a(r))!=null&&d.startsWith("locale_prompt")||(b=a(r))!=null&&b.startsWith("compaction_prompt_locale")||(ae=a(r))!=null&&ae.startsWith("settings:")?"tc-overlay-textarea":"tc-overlay-textarea tc-overlay-textarea-text"),spellcheck:"false"},null,2),[[g,f.value]])])])],32)):I("",!0)]))])])])}}}),xt=Ie(zt,[["__scopeId","data-v-6bafe8a7"]]);export{xt as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ta-messages[data-v-6bafe8a7]{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-6bafe8a7]{color:var(--text-muted);font-size:13px;text-align:center;padding:48px 16px}.ta-msg[data-v-6bafe8a7]{padding:10px 12px;border-radius:10px;font-size:13px;line-height:1.5}.ta-msg-user[data-v-6bafe8a7]{background:var(--accent-dim);align-self:flex-end;max-width:85%}.ta-msg-assistant[data-v-6bafe8a7]{background:var(--glass-highlight);border:1px solid var(--glass-border);align-self:flex-start;max-width:85%}.ta-msg-label[data-v-6bafe8a7]{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.3px;color:var(--text-secondary);margin-bottom:4px}.ta-msg-content[data-v-6bafe8a7]{white-space:pre-wrap;word-break:break-word}.ta-msg-thinking[data-v-6bafe8a7]{color:var(--text-muted);font-style:italic}.ta-input-row[data-v-6bafe8a7]{display:flex;gap:8px}.ta-input[data-v-6bafe8a7]{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-6bafe8a7]:focus{outline:none;border-color:var(--accent)}.ta-input[data-v-6bafe8a7]:disabled{opacity:.5}.btn-send[data-v-6bafe8a7]{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-6bafe8a7]:hover:not(:disabled){opacity:.9}.btn-send[data-v-6bafe8a7]:disabled{opacity:.5;cursor:not-allowed}.checkbox-label[data-v-6bafe8a7]{display:inline-flex!important;align-items:center!important;gap:8px!important;cursor:pointer!important;margin-bottom:0!important}.checkbox-label input[type=checkbox][data-v-6bafe8a7]{width:auto!important;flex-shrink:0!important}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.mgmt-nav[data-v-aa311c53]{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-aa311c53]{max-width:1160px;margin:0 auto;display:flex;align-items:center;gap:14px;padding:10px 16px;height:56px}.nav-back[data-v-aa311c53]{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-aa311c53]:hover{background:var(--accent-dim);border-color:var(--accent);color:var(--accent);transform:scale(1.05)}.nav-back[data-v-aa311c53]:active{transform:scale(.95)}.nav-tabs[data-v-aa311c53]{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-aa311c53]{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-aa311c53]:hover{background:var(--surface-bg);color:var(--text-primary)}.nav-tab.active[data-v-aa311c53]{background:var(--accent);color:#fff;box-shadow:0 2px 12px #0003}.nav-tab svg[data-v-aa311c53]{flex-shrink:0}.nav-controls[data-v-aa311c53]{display:flex;align-items:center;gap:8px}.nav-select[data-v-aa311c53]{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-aa311c53]:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-dim)}.nav-lang-btn[data-v-aa311c53]{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-aa311c53]:hover{background:var(--accent-dim);border-color:var(--accent);color:var(--accent);transform:scale(1.05)}.nav-lang-btn[data-v-aa311c53]:active{transform:scale(.95)}@media(max-width:768px){.mgmt-nav-inner[data-v-aa311c53]{padding:0 12px;gap:8px}.nav-tab[data-v-aa311c53]{padding:7px 10px;font-size:12px;gap:4px}.nav-tab svg[data-v-aa311c53]{width:13px;height:13px}}@media(max-width:480px){.nav-tab span[data-v-aa311c53]{display:none}.nav-tab[data-v-aa311c53]{padding:7px 12px;gap:0}.nav-tab svg[data-v-aa311c53]{width:16px;height:16px;margin:0 auto}.nav-tabs[data-v-aa311c53]{justify-content:center}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as I,V as H,W as K,R as P,I as y,U as R,C as T,$ as E,z as U,H as W,A as i,g as L,d as e,m as b,B as $,K as n,e as h,a0 as d,J as c,y as m,f as g,S as D,c as v,_ as J}from"./index-DVOv8YZP.js";const O={class:"mgmt-nav"},q={class:"mgmt-nav-inner"},F=["title"],G={class:"nav-tabs"},Q={class:"nav-controls"},X=I({__name:"ManagementNav",setup(Y){const C=H(),B=K(),p=P(),{user:S}=y(p),w=R(),{t:o,setLocale:M}=w,{locale:x}=y(w);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:")),V=v(()=>k("manage:tool:")),z=[{value:"light",labelKey:"theme_light"},{value:"dark",labelKey:"theme_dark"}],N=v(()=>z.map(s=>({value:s.value,label:o(s.labelKey)}))),r=T(localStorage.getItem("theme")||"light");function _(s){r.value=s,document.documentElement.setAttribute("data-theme",s),localStorage.setItem("theme",s)}E(r,_),U(async()=>{await p.checkAuth(),_(r.value)});function u(s){return C.path.startsWith(s)}function j(){M(x.value==="zh"?"en":"zh")}return(s,t)=>{const a=W("router-link");return i(),L("nav",O,[e("div",q,[b($),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,F),e("div",G,[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),V.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",Q,[b(D,{modelValue:r.value,"onUpdate:modelValue":t[1]||(t[1]=l=>r.value=l),options:N.value,searchable:!1},null,8,["modelValue","options"]),e("button",{class:"nav-lang-btn",onClick:j},c(n(x)==="zh"?"EN":"中"),1)])])])}}}),ee=J(X,[["__scopeId","data-v-aa311c53"]]);export{ee as M};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as W,U as X,Q as ee,z as le,A as i,g as m,m as D,d as e,J as o,K as a,a1 as c,Z as _,a2 as te,F as V,G as S,S as ae,e as N,a3 as E,Y as oe,f as $,T as I,C as r,c as se,u as ne,M as de,i as ie,p as ue,_ as me}from"./index-DVOv8YZP.js";import{M as re}from"./ManagementNav-DXmOEWXa.js";const ce={class:"mgmt-page"},_e={class:"mgmt-page-content"},ve={class:"mgmt-header"},ge={class:"mgmt-toolbar"},pe=["placeholder"],he={key:0,class:"table-wrap"},be={class:"mgmt-table"},fe={key:1,class:"mgmt-empty"},ye={key:2},ke={class:"table-wrap"},we={class:"mgmt-table"},Ce=["title"],xe=["title"],Me=["title"],Ve={class:"cell-actions"},Se=["onClick"],Ue=["onClick"],Pe={class:"mgmt-pagination"},$e=["disabled"],ze={class:"mgmt-page-info"},Fe=["disabled"],Te={class:"dialog dialog-wide"},Ae={class:"form-group"},Be=["placeholder","disabled"],De={class:"form-group"},Ne=["value"],Ee={class:"form-group"},Ie=["placeholder"],Ke={class:"form-group"},Le=["placeholder"],Qe={class:"form-group"},qe=["placeholder"],Ge={class:"form-group"},Je={class:"locale-section",open:""},Oe={key:0,class:"mgmt-empty",style:{padding:"8px 0","font-size":"13px"}},Ye={key:1,class:"model-list"},Ze={class:"model-item-info"},je={class:"model-item-code"},He={key:0,class:"model-item-ctx"},Re={class:"model-item-actions"},We=["onClick"],Xe=["onClick"],el={class:"dialog-actions"},ll=["disabled"],tl={class:"dialog"},al={class:"form-group"},ol=["placeholder","disabled"],sl={class:"form-group"},nl=["placeholder"],dl={class:"form-group"},il=["placeholder"],ul={class:"form-group"},ml=["placeholder"],rl={class:"dialog-actions"},cl=["disabled"],_l=W({__name:"ProviderManagementPage",setup(vl){const{t}=X(),f=ee(),K=["openai","anthropic"],U=r([]),P=r(!1),w=r(!1),g=r(!1),y=r(!1),n=r({name:"",provider_type:"openai",api_key:"",base_url:"",default_model:"",description:"",models:[]}),p=r(!1),k=r(!1),u=r({id:"",code:"",display_name:"",max_context:0}),C=r(-1),x=r(""),v=r(1),M=r(15),z=r(0),L=[{value:10,label:"10"},{value:15,label:"15"},{value:20,label:"20"},{value:50,label:"50"}],F=se(()=>Math.max(1,Math.ceil(z.value/M.value)));async function h(){P.value=!0;try{const d=await ne({q:x.value,page:v.value,page_size:M.value});U.value=d.items,z.value=d.total}catch(d){f.show("Failed to load providers: "+d.message)}finally{P.value=!1}}function T(){v.value=1,h()}function A(d){v.value=d,h()}function Q(){v.value=1,h()}function q(){y.value=!1,n.value={name:"",provider_type:"openai",api_key:"",base_url:"",default_model:"",description:""},g.value=!0}function G(d){y.value=!0,n.value={...d},g.value=!0}async function J(){if(n.value.name){w.value=!0;try{y.value&&n.value.name?await de(n.value.name,n.value):await ie(n.value),g.value=!1,await h()}catch(d){f.show("Failed to save: "+d.message)}finally{w.value=!1}}}async function O(d){if(await f.confirm(t("mgmt_confirm_delete")))try{await ue(d),await h()}catch(s){f.show("Failed to delete: "+s.message)}}function B(d,s){if(!d)return"-";const l=d.replace("T"," ").substring(0,16);return s?`${l} by ${s}`:l}function Y(){k.value=!1,C.value=-1,u.value={id:"",code:"",display_name:"",max_context:0},p.value=!0}function Z(d){k.value=!0,C.value=d,u.value={...(n.value.models??[])[d]},p.value=!0}function j(){if(u.value.id){if(k.value&&C.value>=0){const d=n.value.models??[];d[C.value]={...u.value},n.value.models=[...d]}else{const d=n.value.models??[];if(d.some(s=>s.id===u.value.id)){f.show(t("mgmt_confirm_delete"));return}n.value.models=[...d,{...u.value}]}p.value=!1}}function H(d){const s=n.value.models??[];n.value.models=s.filter((l,b)=>b!==d)}return le(h),(d,s)=>(i(),m("div",ce,[D(re),e("div",_e,[e("header",ve,[e("h1",null,o(a(t)("mgmt_providers")),1),e("button",{class:"btn-primary",onClick:q},o(a(t)("mgmt_new_provider")),1)]),e("div",ge,[c(e("input",{"onUpdate:modelValue":s[0]||(s[0]=l=>x.value=l),class:"mgmt-search",placeholder:a(t)("mgmt_search_placeholder"),onKeyup:te(T,["enter"])},null,40,pe),[[_,x.value]]),e("button",{class:"btn-search",onClick:T},o(a(t)("mgmt_search")),1)]),P.value?(i(),m("div",he,[e("table",be,[e("thead",null,[e("tr",null,[e("th",null,o(a(t)("mgmt_name")),1),e("th",null,o(a(t)("mgmt_provider_type")),1),e("th",null,o(a(t)("mgmt_base_url")),1),e("th",null,o(a(t)("mgmt_default_model")),1),e("th",null,o(a(t)("mgmt_created_at")),1),e("th",null,o(a(t)("mgmt_actions")),1)])]),e("tbody",null,[(i(),m(V,null,S(5,l=>e("tr",{key:l,class:"mgmt-skeleton-row"},[...s[18]||(s[18]=[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))])])])):U.value.length===0?(i(),m("div",fe,o(x.value?a(t)("mgmt_no_results"):a(t)("mgmt_no_providers")),1)):(i(),m("div",ye,[e("div",ke,[e("table",we,[e("thead",null,[e("tr",null,[e("th",null,o(a(t)("mgmt_name")),1),e("th",null,o(a(t)("mgmt_provider_type")),1),e("th",null,o(a(t)("mgmt_base_url")),1),e("th",null,o(a(t)("mgmt_default_model")),1),e("th",null,o(a(t)("mgmt_created_at")),1),e("th",null,o(a(t)("mgmt_actions")),1)])]),e("tbody",null,[(i(!0),m(V,null,S(U.value,l=>(i(),m("tr",{key:l.name},[e("td",{title:l.name},[e("code",null,o(l.name),1)],8,Ce),e("td",null,[e("code",null,o(l.provider_type),1)]),e("td",null,[e("code",{class:"cell-url",title:l.base_url||"-"},o(l.base_url||"-"),9,xe)]),e("td",null,o(l.default_model||"-"),1),e("td",{class:"cell-audit",title:B(l.created_at,l.created_by)},o(B(l.created_at,l.created_by)),9,Me),e("td",Ve,[e("button",{class:"btn-action",onClick:b=>G(l)},o(a(t)("mgmt_edit")),9,Se),e("button",{class:"btn-action btn-danger",onClick:b=>O(l.name)},o(a(t)("mgmt_delete")),9,Ue)])]))),128))])])]),e("div",Pe,[e("button",{class:"btn-page",disabled:v.value<=1,onClick:s[1]||(s[1]=l=>A(v.value-1))},o(a(t)("mgmt_prev_page")),9,$e),e("span",ze,o(v.value)+" / "+o(F.value),1),e("button",{class:"btn-page",disabled:v.value>=F.value,onClick:s[2]||(s[2]=l=>A(v.value+1))},o(a(t)("mgmt_next_page")),9,Fe),D(ae,{modelValue:M.value,"onUpdate:modelValue":s[3]||(s[3]=l=>M.value=l),modelModifiers:{number:!0},options:L,searchable:!1,onChange:Q},null,8,["modelValue"])])]))]),(i(),N(I,{to:"body"},[g.value?(i(),m("div",{key:0,class:"dialog-overlay",onMousedown:s[11]||(s[11]=E(l=>g.value=!1,["self"]))},[e("div",Te,[e("h2",null,o(y.value?a(t)("mgmt_edit_provider"):a(t)("mgmt_new_provider_title")),1),e("div",Ae,[e("label",null,o(a(t)("mgmt_name")),1),c(e("input",{"onUpdate:modelValue":s[4]||(s[4]=l=>n.value.name=l),placeholder:a(t)("mgmt_placeholder_name"),disabled:y.value},null,8,Be),[[_,n.value.name]])]),e("div",De,[e("label",null,o(a(t)("mgmt_provider_type")),1),c(e("select",{"onUpdate:modelValue":s[5]||(s[5]=l=>n.value.provider_type=l),class:"mgmt-dialog-select"},[(i(),m(V,null,S(K,l=>e("option",{key:l,value:l},o(l),9,Ne)),64))],512),[[oe,n.value.provider_type]])]),e("div",Ee,[e("label",null,o(a(t)("mgmt_api_key")),1),c(e("input",{"onUpdate:modelValue":s[6]||(s[6]=l=>n.value.api_key=l),type:"password",placeholder:a(t)("mgmt_api_key_placeholder")},null,8,Ie),[[_,n.value.api_key]])]),e("div",Ke,[e("label",null,o(a(t)("mgmt_base_url")),1),c(e("input",{"onUpdate:modelValue":s[7]||(s[7]=l=>n.value.base_url=l),placeholder:a(t)("mgmt_base_url_placeholder")},null,8,Le),[[_,n.value.base_url]])]),e("div",Qe,[e("label",null,o(a(t)("mgmt_default_model")),1),c(e("input",{"onUpdate:modelValue":s[8]||(s[8]=l=>n.value.default_model=l),placeholder:a(t)("mgmt_model_placeholder")},null,8,qe),[[_,n.value.default_model]])]),e("div",Ge,[e("label",null,o(a(t)("mgmt_description")),1),c(e("textarea",{"onUpdate:modelValue":s[9]||(s[9]=l=>n.value.description=l),rows:"2"},null,512),[[_,n.value.description]])]),e("details",Je,[e("summary",null,o(a(t)("mgmt_models")),1),(n.value.models??[]).length===0?(i(),m("div",Oe,o(a(t)("mgmt_no_models")),1)):(i(),m("div",Ye,[(i(!0),m(V,null,S(n.value.models,(l,b)=>(i(),m("div",{key:l.id,class:"model-item"},[e("div",Ze,[e("strong",null,o(l.display_name||l.id),1),e("span",je,[e("code",null,o(l.code||l.id),1)]),l.max_context>0?(i(),m("span",He,o(l.max_context.toLocaleString())+" ctx",1)):$("",!0)]),e("div",Re,[e("button",{class:"btn-sm btn-action",onClick:R=>Z(b)},o(a(t)("mgmt_edit")),9,We),e("button",{class:"btn-sm btn-action btn-danger",onClick:R=>H(b)},o(a(t)("mgmt_delete")),9,Xe)])]))),128))])),e("button",{class:"btn-sm btn-primary",style:{"margin-top":"8px"},onClick:Y},o(a(t)("mgmt_add_model")),1)]),e("div",el,[e("button",{class:"btn-action",onClick:s[10]||(s[10]=l=>g.value=!1)},o(a(t)("mgmt_cancel")),1),e("button",{class:"btn-primary",onClick:J,disabled:!n.value.name||w.value},o(w.value?a(t)("mgmt_saving"):a(t)("mgmt_save")),9,ll)])])],32)):$("",!0)])),(i(),N(I,{to:"body"},[p.value?(i(),m("div",{key:0,class:"dialog-overlay",onMousedown:s[17]||(s[17]=E(l=>p.value=!1,["self"]))},[e("div",tl,[e("h2",null,o(k.value?a(t)("mgmt_edit_model"):a(t)("mgmt_add_model")),1),e("div",al,[e("label",null,o(a(t)("mgmt_model_id")),1),c(e("input",{"onUpdate:modelValue":s[12]||(s[12]=l=>u.value.id=l),placeholder:a(t)("mgmt_placeholder_id"),disabled:k.value},null,8,ol),[[_,u.value.id]])]),e("div",sl,[e("label",null,o(a(t)("mgmt_model_code")),1),c(e("input",{"onUpdate:modelValue":s[13]||(s[13]=l=>u.value.code=l),placeholder:a(t)("mgmt_model_placeholder_code")},null,8,nl),[[_,u.value.code]])]),e("div",dl,[e("label",null,o(a(t)("mgmt_model_display_name")),1),c(e("input",{"onUpdate:modelValue":s[14]||(s[14]=l=>u.value.display_name=l),placeholder:a(t)("mgmt_placeholder_display_name")},null,8,il),[[_,u.value.display_name]])]),e("div",ul,[e("label",null,o(a(t)("mgmt_model_max_context")),1),c(e("input",{"onUpdate:modelValue":s[15]||(s[15]=l=>u.value.max_context=l),type:"number",min:"0",placeholder:a(t)("mgmt_model_placeholder_context")},null,8,ml),[[_,u.value.max_context,void 0,{number:!0}]])]),e("div",rl,[e("button",{class:"btn-action",onClick:s[16]||(s[16]=l=>p.value=!1)},o(a(t)("mgmt_cancel")),1),e("button",{class:"btn-primary",onClick:j,disabled:!u.value.id},o(a(t)("mgmt_save")),9,cl)])])],32)):$("",!0)]))]))}}),hl=me(_l,[["__scopeId","data-v-aa263a36"]]);export{hl as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.mgmt-dialog-select[data-v-aa263a36]{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-aa263a36]:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-dim)}.model-list[data-v-aa263a36]{display:flex;flex-direction:column;gap:6px}.model-item[data-v-aa263a36]{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;background:var(--glass-highlight);border:1px solid var(--glass-border);border-radius:8px;gap:8px}.model-item-info[data-v-aa263a36]{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.model-item-code[data-v-aa263a36]{color:var(--text-muted);font-size:12px}.model-item-ctx[data-v-aa263a36]{color:var(--accent);font-size:11px;white-space:nowrap}.model-item-actions[data-v-aa263a36]{display:flex;gap:4px;flex-shrink:0}.btn-sm[data-v-aa263a36]{padding:4px 10px;font-size:12px;border-radius:6px;border:1px solid var(--glass-border);background:var(--glass-highlight);color:var(--text-primary);cursor:pointer;transition:opacity .15s}.btn-sm[data-v-aa263a36]:hover{opacity:.8}
|