<!-- 文档分类: IMPL-RPT-009 | 阶段: 实现 | 原路径: docs/修复记录/特性访问与代理解耦-2026-06-01.md -->
# Feature Access 收口与代理/登录解耦修复记录

**日期**: 2026-06-01
**范围**: `khy claude` 代理启动链路、登录边界集中化、feature naming 单一来源

---

## 1. 修复目标

这一轮修复主要解决三类问题：

1. `khy claude` 的可用性不应该依赖 KHY 账号登录。
2. 代理/IDE/管理入口的登录边界不能散落在各个 handler 和 adapter 中。
3. feature key 与 fallback label 不能在多层代码里重复手写，否则后续极易漂移。

对应的工程目标是：

- **高内聚**：登录策略集中到 `authGuard`。
- **低耦合**：`khy claude` 与外层完整 CLI / router / auth 链路解耦。
- **最小改动**：不重写整套架构，只沿现有链路切最小落点。
- **单一来源**：feature key / family prefix / label 尽量只从一处生成。

---

## 2. 原始问题

### 2.1 `khy claude` 代理启动链路耦合过深

原先 `khy claude` 需要经过外层 `khy proxy start` 风格的链路，隐含依赖：

- Python launcher
- Node CLI bridge
- router
- proxy handler
- 某些未来可能变化的 auth / bootstrap 行为

这会带来两个风险：

1. 代理是本地能力，却被外层 CLI 语义绑住。
2. 一旦 router / handler / 登录逻辑变化，`khy claude` 可能被无关改动误伤。

### 2.2 登录判断散落在多层入口

修复前存在多处直接 `requireLogin(...)`：

- `gateway relay`
- `webRelayAdapter`
- `clipboardRelayAdapter`
- 某些 IDE / 管理入口逻辑

结果是：

- 谁该登录、谁不该登录，没有统一策略面。
- 改一个入口很容易漏掉另一个入口。
- 行为判断和业务代码耦合在一起，不利于维护。

### 2.3 feature key / label 多处手写

例如：

- `gateway.relay`
- `gateway.manage.open`
- `proxy.relay.web`
- `claude.launch`
- `'gateway manage'`
- `'web relay'`

这些字符串一旦分散在：

- `authGuard`
- handler
- adapter
- 测试

就会出现两个长期问题：

1. 命名漂移：策略层和调用层不一致。
2. 小模型/新维护者容易在错误位置补字符串，越修越散。

---

## 3. 修复方法

### 3.1 先把 `khy claude` 从外层代理 CLI 链路中解耦

核心思路不是大改架构，而是让 `khy claude` 优先直接拉起后端代理守护进程：

- 优先直接启动 `backend/scripts/proxy-daemon.js`
- 只有直接启动不可用时，才回退到原 CLI 方式

这样做的好处：

- `khy claude` 不再依赖完整外层命令分发链路。
- 反向代理能力回归为“本地 runtime 能力”。
- 即使未来 CLI 登录或 router 调整，`khy claude` 仍更稳定。

### 3.2 用 `authGuard` 收口登录边界

新增并推广统一入口：

- `getFeatureAccess(featureKey, fallbackLabel)`
- `requireFeatureAccess(featureKey, fallbackLabel)`

策略统一集中在 `authGuard`：

- `gateway.relay.*` → 需要登录
- `gateway.manage.*` → 不需要登录
- `proxy.*` → 不需要登录
- `claude|codex|cursor|kiro|trae.*` → 不需要登录
- 未匹配 feature → 默认需要登录

这一步的关键不是“让所有入口都免登录”，而是：

- **谁应该登录**，由策略层声明。
- **业务入口只消费策略**，不再自己做分散判断。

### 3.3 把调用侧逐步切到 `requireFeatureAccess(...)`

被收口的入口包括：

- `gateway relay`
- `gateway manage`
- `webRelayAdapter`
- `clipboardRelayAdapter`
- IDE 命令入口

这一步完成后，`backend/src` 中不再保留散落在业务层的 `requireLogin(...)` 调用，`requireLogin` 只保留在 `authGuard` 内部作为策略执行细节。

### 3.4 引入 `featureKeyBuilder`，把命名逻辑从策略层和调用层拿出来

新增 `backend/src/services/featureKeyBuilder.js`，分阶段承接：

1. key builder
2. label builder
3. family prefix builder
4. family regex builder
5. prefix registry

它现在承担三类职责：

- **命名生成**：如 `gateway.manage.status`、`proxy.relay.web`
- **标签生成**：如 `gateway manage`、`web relay`
- **family 规则生成**：如 `gateway/manage`、`proxy/root`、`ide/families`

### 3.5 再把 family 前缀做成 registry

目前 registry 已收口到 builder：

- `gateway.root / relay / manage`
- `proxy.root / relay`
- `ide.families`

策略层和部分调用层已经改为直接用：

- `getFeatureFamilyPrefix(...)`
- `buildFeatureFamilyPrefixRegex(...)`
- `joinFeatureKey(...)`

这意味着：

- `authGuard` 基本不再知道具体前缀长什么样。
- `gateway/proxy` 的部分调用侧也不再依赖专用 helper。

### 3.6 标记“内部兼容层 helper”

为了不一次性强推全量替换，仍保留一组专用 helper，但明确标记为：

- **内部兼容层**
- **后续新代码不应优先依赖**

推荐新代码优先使用：

- `getFeatureFamilyPrefix(...)`
- `buildFeatureFamilyPrefixRegex(...)`
- `joinFeatureKey(...)`

这样既避免大爆炸式重构，又给后续维护设了清晰边界。

---

## 4. 最终行为结果

### 4.1 登录边界

当前策略结果：

- `proxy.*`：免登录
- `gateway.manage.*`：免登录
- `claude|codex|cursor|kiro|trae.*`：免登录
- `gateway.relay.*`：需要登录
- 未知 feature：默认需要登录

### 4.2 `khy claude` 可用性

当前行为目标已经达成：

- `khy claude` 可以在**没有 KHY 账号登录**时使用本地代理能力。
- 代理优先作为本地守护能力启动，而不是被完整外层 CLI 链路绑死。

### 4.3 维护边界

当前维护原则已经从代码层面落下：

- 登录策略改动优先改 `authGuard`
- feature naming 改动优先改 `featureKeyBuilder`
- 新代码应优先使用 family API，不继续扩散专用 helper

---

## 5. 涉及文件

| 文件 | 作用 |
|------|------|
| `khy_platform/cli.py` | `khy claude` 启动链路解耦，优先直启代理守护进程 |
| `unit_tests/test_claude_proxy_bootstrap.py` | Python 侧代理启动链路回归测试 |
| `backend/src/services/authGuard.js` | 登录策略集中化 |
| `backend/src/services/featureKeyBuilder.js` | feature naming / family prefix / label / registry 单一来源 |
| `backend/src/cli/handlers/gateway.js` | `gateway relay` / `gateway manage` 接入统一策略 |
| `backend/src/cli/handlers/ide.js` | IDE 入口接入统一策略 |
| `backend/src/services/gateway/adapters/webRelayAdapter.js` | Web relay 接入统一策略 |
| `backend/src/services/gateway/adapters/clipboardRelayAdapter.js` | Clipboard relay 接入统一策略 |
| `backend/tests/security/authGuard.exports.test.js` | 登录策略与边界测试 |
| `backend/tests/security/featureKeyBuilder.test.js` | naming / registry / family API 测试 |
| `backend/tests/gateway/relayAccessBoundary.test.js` | relay 调用侧边界测试 |
| `backend/tests/cli/ide.access.test.js` | IDE 入口边界测试 |

---

## 6. 验证方法

本轮修复使用的是**最小验证闭环**，避免把无关模块一起拉进来：

### Python 侧

```bash
python3 -m py_compile khy_platform/cli.py
python3 -m unittest unit_tests.test_claude_proxy_bootstrap unit_tests.test_proxy_endpoint_discovery
```

### Node / JS 侧

```bash
node --check backend/src/services/authGuard.js
node --check backend/src/services/featureKeyBuilder.js
node --check backend/src/cli/handlers/gateway.js
node --check backend/src/cli/handlers/ide.js
node --check backend/src/services/gateway/adapters/webRelayAdapter.js
node --check backend/src/services/gateway/adapters/clipboardRelayAdapter.js
npm --prefix backend test -- --runTestsByPath \
  tests/security/featureKeyBuilder.test.js \
  tests/security/authGuard.exports.test.js \
  tests/gateway/relayAccessBoundary.test.js \
  tests/cli/ide.access.test.js
```

验证目标不是“把全仓库都跑一遍”，而是：

- 改哪一层，就验证哪一层
- 先保证边界正确，再考虑扩大验证面

---

## 7. 这次修复采用的方法论

### 7.1 最小落点

没有直接重写代理系统、路由系统或权限系统，而是按下面顺序逐层收口：

1. 先解除最危险耦合点：`khy claude` → 外层代理 CLI
2. 再集中登录策略
3. 再统一调用入口
4. 再统一命名来源
5. 最后给兼容 helper 做边界标记

### 7.2 先收口，再抽象

不是先设计“大而全的权限框架”，而是先把散点收回来，再一点点抽象成：

- `requireFeatureAccess(...)`
- `featureKeyBuilder`
- `FEATURE_PREFIX_REGISTRY`

这样可以避免一次性过度设计。

### 7.3 兼容层保留，但禁止继续扩散

本轮没有强制移除所有旧 helper，而是：

- 保留兼容包装函数
- 在 builder 内明确标注其性质
- 把新代码引导到通用 family API

这比“一口气全部替换”更稳，也更适合脏工作区和持续演进中的代码库。

---

## 8. 后续维护建议

后续如果还要继续沿这条线优化，建议遵守以下顺序：

1. 新增 feature 时，先补 `featureKeyBuilder`，不要先在 handler 里手写字符串。
2. 新增登录边界时，先补 `authGuard` 规则，再接入调用侧。
3. 新代码优先使用：
   - `getFeatureFamilyPrefix(...)`
   - `buildFeatureFamilyPrefixRegex(...)`
   - `joinFeatureKey(...)`
4. 只有为了兼容旧代码时，才继续使用内部兼容层 helper。
5. 如果某个兼容 helper 已无外部必要依赖，可以在后续单独清理，但不要和业务改动混在同一轮里做。

---

## 9. 总结

这次修复本质上不是“加了一些 helper”，而是把原本分散在：

- Python launcher
- CLI handler
- adapter
- auth boundary
- 测试

中的三种职责重新分层：

- **运行时启动职责**：交给最短链路处理
- **登录策略职责**：交给 `authGuard`
- **命名职责**：交给 `featureKeyBuilder`

最终效果是：

- `khy claude` 更独立
- 代理与 IDE 入口边界更清晰
- feature naming 更集中
- 小模型或新维护者更不容易把范围改散

