<!-- 文档分类: IMPL-RPT-018 | 阶段: 实现 | 原路径: docs/指南/管理前端自动可用修复-2026-05-31.md -->
# 管理前端自动可用修复

> 日期: 2026-05-31 | 影响: `khy gateway manage`

## 问题

`khy gateway manage` 启动管理界面时，前端始终显示 "未就绪（pip 安装暂不含预构建前端）"，
即使 `ai-frontend/dist/` 已有完整构建产物。

```
前端: 未就绪
前端原因: unavailable
推荐入口: API 直管（当前无可用前端）
```

## 根因分析

三处断裂导致前端不可用：

### 断裂 1: setup.py 不打包 ai-frontend/

`BuildWithBundle.run()` 只复制 `frontend/dist/`（旧路径，已不存在），
从未打包 `ai-frontend/` 源码或预构建产物。pip 安装后目标机器上没有前端文件。

### 断裂 2: _bootstrap.py 不装 ai-frontend 依赖

runtime `npm install` 只针对 `backend/`，从未对 `ai-frontend/` 执行。
即使前端源码存在，缺少 `node_modules` 导致 Vite dev server 无法启动。

### 断裂 3: daemon 不回退到静态 dist

`ai-manage-daemon.js` 存在以下问题：
- 不解析 `--frontend-dist-dir` 参数（spawner 传了但 daemon 忽略了）
- 不调用 `aiManagementServer.configureFrontendStatic()`（函数已导出但从未被使用）

当 `node_modules` 缺失时，`noAutoFrontend` 自动设为 `true`，
`resolveFrontend()` 跳过 Vite dev server，外部端口探测也失败，结果 `frontendAvailable = false`。

## 修复

### 1. setup.py — 打包 ai-frontend/

在 `BuildWithBundle.run()` 中添加 `ai-frontend/` copytree：

```python
ai_frontend_src = ROOT / "ai-frontend"
ai_frontend_dst = bundled / "ai-frontend"
if ai_frontend_src.exists():
    shutil.copytree(
        ai_frontend_src, ai_frontend_dst,
        ignore=shutil.ignore_patterns(*EXCLUDE_PATTERNS, 'node_modules'),
    )
```

`MANIFEST.in` 同步添加 `recursive-include ai-frontend *` + `prune ai-frontend/node_modules`。

### 2. _bootstrap.py — ai-frontend npm install

在 `_ensure_npm_install()` 的 `finally` 块中添加 ai-frontend 依赖安装：

```python
ai_frontend_dir = backend_dir.parent / "ai-frontend"
if ai_frontend_dir.exists() and (ai_frontend_dir / "package.json").exists():
    nm = ai_frontend_dir / "node_modules"
    lock = nm / ".package-lock.json"
    if not _path_exists_safe(nm) or not _path_exists_safe(lock):
        subprocess.run(
            [npm_cmd, "install", "--no-audit", "--no-fund"],
            cwd=str(ai_frontend_dir), timeout=_NPM_TIMEOUT,
            **_subprocess_kwargs(),
        )
```

### 3. ai-manage-daemon.js — 解析参数 + 静态回退

解析 `--frontend-dist-dir` 参数：
```js
const frontendDistDir = parseStringArg(argv, '--frontend-dist-dir', '');
```

`resolveFrontend()` 失败后回退到静态托管：
```js
if (!frontend.available && frontendDistDir) {
    const staticResult = aiManagementServer.configureFrontendStatic({
        distDir: frontendDistDir,
    });
    if (staticResult.enabled) {
        frontend.available = true;
        frontend.managed = true;
        frontend.port = apiPort;        // 静态文件从 API 端口提供
        frontend.reason = 'static-dist';
    }
}
```

### 4. gateway.js — 修正提示信息

移除硬编码的 "pip 安装暂不含预构建前端" 文案，改为通用 "管理前端未就绪"。

## 修复后行为

| 场景 | 前端来源 | 端口 |
|------|---------|------|
| 有 `node_modules` | Vite dev server | 8090 |
| 无 `node_modules`，有 `dist/` | `configureFrontendStatic()` 静态托管 | 9090 (API 同端口) |
| pip 安装首次启动 | runtime `npm install` 后 Vite dev server | 8090 |

完整链路：

```
pip install → 打包 ai-frontend/ (源码 + dist/)
→ runtime _bootstrap.py → cd ai-frontend && npm install
→ khy gateway manage → daemon → 优先 Vite dev server (8090)
                              → 回退 configureFrontendStatic (9090/admin/ai-gateway)
                              → 前端可用
```

## 修改文件

| 文件 | 改动 |
|------|------|
| `setup.py` | `BuildWithBundle.run()` 添加 ai-frontend copytree |
| `MANIFEST.in` | 添加 ai-frontend include + prune node_modules |
| `khy_platform/_bootstrap.py` | `_ensure_npm_install()` finally 块安装 ai-frontend 依赖 |
| `backend/scripts/ai-manage-daemon.js` | 解析 `--frontend-dist-dir` + 静态回退 |
| `backend/src/cli/handlers/gateway.js` | 修正 "未就绪" 提示信息 |

## 验证

```bash
# 正常模式（有 node_modules）
khy gateway manage
# → 前端: http://127.0.0.1:8090

# 模拟 pip 场景（无 node_modules）
mv ai-frontend/node_modules ai-frontend/_bak
khy gateway manage stop && khy gateway manage start --daemon
# → 前端: http://127.0.0.1:9090 (静态 dist 回退)
curl -s http://127.0.0.1:9090/admin/ai-gateway | head -1
# → <!DOCTYPE html>

# 恢复
khy gateway manage stop
mv ai-frontend/_bak ai-frontend/node_modules
```
