# https://github.com/Sir-chawakorn/sanook-cli 项目说明书

生成时间：2026-06-21 02:49:10 UTC

## 目录

- [项目概述、快速开始与整体架构](#page-overview)
- [代理循环、工具沙箱与模型提供商](#page-agent)
- [分层记忆系统与第二大脑工作区](#page-memory)
- [网关、消息通道、MCP、技能与本地面板](#page-extensibility)

<a id='page-overview'></a>

## 项目概述、快速开始与整体架构

### 相关页面

相关主题：[代理循环、工具沙箱与模型提供商](#page-agent), [分层记忆系统与第二大脑工作区](#page-memory), [网关、消息通道、MCP、技能与本地面板](#page-extensibility)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)
- [second-brain/README.md](https://github.com/Sir-chawakorn/sanook-cli/blob/main/second-brain/README.md)
- [src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
- [src/providers/codex.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/codex.ts)
- [src/providers/models.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/models.ts)
- [src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)
- [src/dashboard/api-helpers.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/api-helpers.ts)
- [src/dashboard/api-helpers.test.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/api-helpers.test.ts)
- [src/providers/registry.test.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.test.ts)
- [src/eval/run.test.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/eval/run.test.ts)
</details>

# 项目概述、快速开始与整体架构

## 一、项目定位与核心特性

sanook-cli 是一款「**自带密钥（BYOK）的终端 AI 编程 agent**」，使用 TypeScript 从零构建，作为 Claude Code 的替代品聚焦本地化、跨 session 记忆与模型可移植性。`package.json` 中的描述明确指出其覆盖 BYOK、9 家 LLM 提供方、MCP、cron 网关、技能系统与 Git 感知五大能力面。资料来源：[package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)

核心特性可以归纳为四条主线：

- **多模型可移植**：通过 Vercel AI SDK 抽象统一接入 Anthropic、Google、OpenAI、xAI、Mistral、Groq 等云端服务，并支持 Ollama / LM Studio 本地推理，以及将执行委托给 `codex` CLI 的子代理模式。资料来源：[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
- **跨会话记忆**：`sanook brain` 命令在本地生成 Obsidian vault 骨架，覆盖 `Projects/`、`Sessions/`、`Shared/`、`Intake/`、`Runbooks/`、`Skills/` 等目录，赋予 AI 长期记忆与知识管线。资料来源：[second-brain/README.md](https://github.com/Sir-chawakorn/sanook-cli/blob/main/second-brain/README.md)
- **本地 Web Dashboard**：除终端 REPL 外，还内置 SSE 流式 Agent Console 与基于 `ws + node-pty` 的可选 PTY Web Shell。资料来源：[src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)
- **插件与扩展**：通过 `skills/` 目录、`mcp` 关键词以及可执行 `npm run eval` 的 eval 入口，支持技能系统、Model Context Protocol 与模型能力回归评测。资料来源：[package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)

## 二、快速开始

### 1. 环境与安装

| 项目 | 要求 | 资料来源 |
|---|---|---|
| Node.js | `>=22`（`engines.node` 声明） | [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json) |
| 命令入口 | `sanook`、`sanookai` → `dist/bin.js` | [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json) |
| 推荐安装 | `npm install -g sanook-cli` 或 `npx sanook-cli` | [src/dashboard/api-helpers.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/api-helpers.ts) |
| 可选依赖 | `node-pty`、`ws`（用于 Dashboard Web Shell） | [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json) |
| 开发运行 | `npm run dev`（`tsx src/bin.ts`） | [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json) |

发布构建流程为：清理 `dist/` → `tsc -p tsconfig.build.json` → 复制 dashboard 静态资源 → `chmod 755 dist/bin.js`，并通过 `prepublishOnly` 钩子强制在 `npm publish` 前重新构建。资料来源：[package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)

### 2. 第一次交互

1. 启动 `sanook` 后，`hasUsableEnvKey()` 会先校验环境变量里是否已经存在「可用」API key（含 OAuth/subscription token 拒绝策略）。无 key 或仅 OAuth 时引导用户进入首次运行向导。资料来源：[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
2. 在 REPL 中可通过 `provider:model` 形式的 spec 切换模型，例如 `openai:gpt-5.5`、`anthropic:sonnet`、`groq:fast`，`canonicalSpec()` 在写入 UI 状态前会自动归一化。资料来源：[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
3. 开启 Dashboard 时，浏览器侧可访问 `/api/terminal/run`，前端通过 SSE 接收 `AgentEvent`，并按 `sessionId` 在服务端保留 `ModelMessage[]` 多轮历史（最多 20 个会话）。资料来源：[src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)

## 三、整体架构

sanook-cli 在概念上由四层组成：**CLI 入口与参数解析 → Agent 循环 → 提供方适配层 → 持久化与扩展层**。

```mermaid
flowchart TD
    A[CLI 入口 dist/bin.js] --> B[首次运行向导 / 配置]
    B --> C[REPL 主循环]
    B --> D[Web Dashboard /api/terminal/run]
    C --> E[Agent Loop /runAgent]
    D --> E
    E --> F[Provider 适配层 PROVIDERS]
    F --> F1[Anthropic / Google / OpenAI / xAI / Mistral / Groq]
    F --> F2[本地 Ollama / LM Studio]
    F --> F3[Codex 子进程委托]
    E --> G[Skills / MCP / 工具调用]
    E --> H[Second-Brain Vault]
    H --> I[Obsidian Shared/ + Sessions/]
```

架构要点说明：

- **Provider Registry 是单一事实源**：新增厂商只需在 `PROVIDERS` 表中追加一条 `ProviderConfig`（含 `id / envVar / requiresKey / models / create()`），循环与成本逻辑无需改动。资料来源：[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
- **Key 策略统一校验**：`assertDirectApiKey()` 配合 `keyFormat` 正则会拦截 `sk-ant-oat*`、`ya29.*` 等 OAuth 凭据，避免使用 ChatGPT/Codex/Claude 订阅 token 触发 ToS 风险。资料来源：[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
- **动态模型发现**：`listModelsFor()` 针对 Google 走 `generativelanguage.googleapis.com`，对 Anthropic 走 `x-api-key` 头，其他云端走 `/models` 端点，并通过 `AbortController` 限时避免挂起。资料来源：[src/providers/models.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/models.ts)
- **Codex 委托模式**：`runCodex()` 通过 `codex exec` 启动非交互式子进程，解析 JSONL 事件并支持 `cwd` 隔离与 `resumeThreadId`，兼容 `--json` 被工具激活时忽略的已知 bug。资料来源：[src/providers/codex.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/codex.ts)
- **Dashboard 优雅降级**：`shellStatus()` 会探测 `node-pty` 与 `ws` 是否同时可用，缺失时返回 `available: false` 并附带原因，UI 不会因此崩溃。资料来源：[src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)

## 四、测试与质量保障

项目使用 Vitest 作为唯一测试框架，关键契约均有对应单测覆盖：

- **Provider 注册表**：验证 alias 解析（`gpt` → `openai:gpt-5.5`）与 `canonicalSpec()` 在 UI 状态写入前的归一化行为，同时保证 OAuth 凭据在 embedder 路径上被降级为 `null`。资料来源：[src/providers/registry.test.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.test.ts)
- **Dashboard 元数据**：确认 `npm` 是唯一 `ready=true` 且被推荐的安装方式，`curl / homebrew / winget` 都标注为尚未就绪并附带部署原因。资料来源：[src/dashboard/api-helpers.test.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/api-helpers.test.ts)
- **Eval 模型解析**：`evalModelFromEnv()` 会对 `SANOOK_MODEL` 去空白，缺省回退到 `sonnet`，与 `npm run eval`（`tsx src/eval/run.ts`）形成闭环。资料来源：[src/eval/run.test.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/eval/run.test.ts) · [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)

发布前可执行 `npm run typecheck` 保证 TypeScript 干净，再跑 `npm test` 验证关键不变量。

## 另请参阅

- `second-brain/README.md` — Obsidian vault 模板与 AI Routing Contract
- `src/providers/registry.ts` — Provider 注册表与 Key Policy 详细说明
- `src/dashboard/terminal.ts` — Dashboard Agent Console 与 Web Shell 行为细节
- `package.json` — scripts、bin、files、keywords 的完整定义

---

<a id='page-agent'></a>

## 代理循环、工具沙箱与模型提供商

### 相关页面

相关主题：[项目概述、快速开始与整体架构](#page-overview), [分层记忆系统与第二大脑工作区](#page-memory)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/loop.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/loop.ts)
- [src/agentContext.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/agentContext.ts)
- [src/approval.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/approval.ts)
- [src/checkpoint.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/checkpoint.ts)
- [src/compaction.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/compaction.ts)
- [src/sandbox.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/sandbox.ts)
- [src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
- [src/providers/models.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/models.ts)
- [src/providers/codex.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/codex.ts)
- [src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)
- [src/eval/run.test.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/eval/run.test.ts)
</details>

# 代理循环、工具沙箱与模型提供商

sanook-cli 是一个 BYOK（自带密钥）的终端 AI 编程代理，本页聚焦其三大核心机制：**代理循环（agent loop）**、**工具沙箱与审批**、**多模型提供商注册表**。三者通过 `runAgent` 入口协同：循环驱动模型推理，模型返回的 tool_call 被沙箱拦截，审批通过后落地执行，结果再回到循环中继续。

## 整体架构与事件流

代理循环是整个系统的"心脏"。仪表板后端把 REPL 的体验复刻到了 Web 端，证据见 `src/dashboard/terminal.ts`：它显式导入 `runAgent, type AgentEvent` 并把代理事件以 SSE 形式流式转发给浏览器，包括文本、推理、工具调用、🧠 记忆事实以及 ✨ 自动生成的技能 资料来源：[src/dashboard/terminal.ts:1-12]()。每个浏览器会话在后端维护独立的 `ModelMessage[]` 历史，上限 20 个并发会话 资料来源：[src/dashboard/terminal.ts:5-8]()。

```mermaid
flowchart LR
    A[用户输入] --> B[runAgent<br/>src/loop.ts]
    B --> C{模型提供商<br/>registry.ts}
    C -- text/reasoning --> D[事件流 SSE]
    C -- tool_call --> E[沙箱校验<br/>sandbox.ts]
    E --> F{需审批?<br/>approval.ts}
    F -- 是 --> G[checkpoint<br/>回滚锚点]
    F -- 否 --> H[执行工具]
    H --> I[结果回灌<br/>agentContext.ts]
    I --> B
    D --> J[REPL / Web 终端]
    G --> H
```

## 工具沙箱、审批与检查点

任何模型发起的工具调用都必须穿越沙箱和审批两道关卡。`src/sandbox.ts` 负责把文件系统与命令执行约束在工作区范围内；`src/approval.ts` 决定该调用是否需要人类确认（写操作、对外网络、危险命令通常需要）。一旦执行开始，`src/checkpoint.ts` 记录回滚锚点——若后续步骤失败或用户拒绝，可以从锚点恢复。`src/agentContext.ts` 则把工具结果、记忆、技能等"上下文增量"累积起来，供下一轮循环使用。当上下文接近模型窗口上限时，`src/compaction.ts` 触发摘要压缩，保留关键事实与最近消息。

## 模型提供商注册表

`src/providers/registry.ts` 是多模型支持的"单一事实源"。它导出一个 `PROVIDERS` 表，每个条目描述一家提供商的鉴权方式、基础 URL、支持的模型别名以及工厂函数 资料来源：[src/providers/registry.ts:1-25]()。模型规范（spec）支持三种写法：`provider:model`、`provider:alias`、或裸别名（如 `gpt`、`haiku`、`sonnet`），`parseSpec` 与 `canonicalSpec` 把它们规范化为 `provider:model-id` 资料来源：[src/providers/registry.ts:79-96]()。

提供商分两类：直接 API（如 `anthropic`、`openai`、`xai`、`mistral`、`groq`）和委托型 `codex`，后者不持有 key，而是依赖本地 `codex` CLI 的登录状态 资料来源：[src/providers/codex.ts:1-25]()。`hasUsableEnvKey` 会拒绝 OAuth/订阅型 token，避免把 ChatGPT 订阅 key 误当作 API key 使用 资料来源：[src/providers/registry.ts:107-119]()。

`fastSibling(spec)` 为循环内的"轻量任务"（摘要、压缩）选择同家的快速模型——比如 `anthropic` 自动落到 `haiku`、`google` 落到 `flash`——无快档则原样返回 资料来源：[src/providers/registry.ts:6-13]()。当用户希望使用"非别名"的新模型时，可通过 `provider:full-id` 直接覆盖 资料来源：[src/providers/registry.ts:18-25]()。

## 远程模型发现与评估回路

`src/providers/models.ts` 的 `listRemoteModels` 直接调用各家 `/models` 端点，把权威模型 ID 拉回本地——`google` 用 query key、`anthropic` 用 `x-api-key`、OpenAI 兼容用 `Bearer`，失败/超时/本地模型则返回 `[]`，由调用方回退到 curated 别名 资料来源：[src/providers/models.ts:1-25]()。评估脚本读取 `SANOOK_MODEL` 环境变量，`trim` 后空白则回退到 `sonnet` 资料来源：[src/eval/run.test.ts:1-10]()，确保离线评估永远有可用的目标模型。

## 常见失败模式

- **OAuth key 误用**：将 `sk-ant-oat…` 之类的订阅 token 设为 `ANTHROPIC_API_KEY` 时，`assertDirectApiKey` 抛错并提示去控制台取真 key 资料来源：[src/providers/registry.ts:112-118]()。
- **不支持的提供商**：解析到未知 provider 时，错误信息列出所有受支持的 provider 名称 资料来源：[src/providers/registry.ts:55-60]()。
- **Codex 未登录**：委托型 provider 需要 `codex login`，否则 `runCodex` 给出明确安装/登录指引 资料来源：[src/providers/codex.ts:6-20]()。
- **SSE 连接断开**：`sseSend` 会检测 `res.destroyed`/`writableEnded`，避免向已关闭的 socket 写入 资料来源：[src/dashboard/terminal.ts:9-15]()。

## See Also

- [提供商与鉴权矩阵](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
- [Web 终端后端](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)
- [第二大脑 / 跨会话记忆](https://github.com/Sir-chawakorn/sanook-cli/blob/main/second-brain/README.md)

---

<a id='page-memory'></a>

## 分层记忆系统与第二大脑工作区

### 相关页面

相关主题：[项目概述、快速开始与整体架构](#page-overview), [代理循环、工具沙箱与模型提供商](#page-agent)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/memory.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/memory.ts)
- [src/memory-store.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/memory-store.ts)
- [src/memory-log.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/memory-log.ts)
- [src/turn-retrieval.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/turn-retrieval.ts)
- [src/tools/remember.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/tools/remember.ts)
- [src/tools/recall.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/tools/recall.ts)
- [second-brain/README.md](https://github.com/Sir-chawakorn/sanook-cli/blob/main/second-brain/README.md)
- [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)
- [src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)
</details>

# 分层记忆系统与第二大脑工作区

## 概述

`sanook-cli` 是一个面向终端的 AI 编程 agent,主打"BYOK(自带密钥)+跨会话记忆"两大能力。其记忆体系由两层组成:**运行时分层记忆**(短/长期事实、回合检索)与**第二大脑工作区**(Obsidian 风格的知识库脚手架)。两者协同工作,使 agent 在不同会话之间能保留上下文,并将对话成果沉淀为可被人类检索的结构化笔记。

`package.json` 的关键字同时列出 `cross-session-memory`、`second-brain`、`obsidian`,且将 `second-brain/` 目录随 npm 包一并发布,说明该项目将"长期记忆"作为一等公民功能交付。资料来源:[package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)

---

## 第二大脑工作区:目录契约

第二大脑本质是一份可被 Obsidian 直接打开的 vault 脚手架,由 `sanook brain` 命令在用户本地生成。`second-brain/README.md` 描述了 16 个固定目录,每个目录都有明确的"放置什么 / 禁止放置什么"约定,agent 在写入前必须先查阅对应 `_Index.md` 的 **AI Routing Contract**。

### 核心目录分层

| 层级 | 目录 | 角色 |
|---|---|---|
| 执行层 | `Projects/` `Sessions/` `Intake/` `Runbooks/` `Bugs/` `Handoffs/` | 进行中的工作流、每日 AI 日志 |
| 中枢层 | `Shared/` `Shared/Memory/` `Goals/` `Areas/` | 记忆、规则、决策、长期方向 |
| 知识层 | `Research/` `Learning/` `Distillations/` | 研究、读书、提炼 |
| 前沿层 | `Skills/` `Playbooks/` `Evals/` `Entities/` | 自改进技能与剧本 |

资料来源:[second-brain/README.md](https://github.com/Sir-chawakorn/sanook-cli/blob/main/second-brain/README.md)

这种"P/A/R (Projects / Areas / Resources)+ 知识流水线"的分层源自 PARA 方法论,但额外增加了 `Skills/` `Playbooks/` `Evals/` 三个"自演进"目录,使 vault 本身具备"读后自我增强"的能力。

---

## 运行时分层记忆

运行时的记忆由 `src/memory.ts`、`src/memory-store.ts`、`src/memory-log.ts` 与 `src/turn-retrieval.ts` 共同维护,暴露为两个工具:

- **🧠 `remember`**(`src/tools/remember.ts`):把事实持久化为长期记忆条目。
- **✨ `recall`**(`src/tools/recall.ts`):在新一轮对话开始前,从记忆库中检索相关上下文注入 prompt。

`src/turn-retrieval.ts` 负责在每回合推理前自动召回(top-k 向量/关键词混合检索),而 `src/memory-log.ts` 记录追加历史以便审计与回滚。

### 数据流

```mermaid
flowchart LR
    U[用户输入] --> REPL[REPL/Agent loop]
    REPL --> TR[turn-retrieval<br/>自动召回]
    TR --> MS[(memory-store<br/>长期事实)]
    REPL --> R[tools/remember]
    R --> ML[memory-log]
    MS --> SB[second-brain vault<br/>Shared/Memory]
    REPL --> DASH[Dashboard SSE<br/>forward 🧠/✨]
```

Dashboard 后端 `src/dashboard/terminal.ts` 在转发 agent 事件时,会把 `remember` 写入的事实和 `recall` 自动创建的新技能通过 SSE 推送到浏览器,使网页控制台与终端 REPL 行为一致。资料来源:[src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)

---

## 配置与密钥解析

记忆系统不直接持有任何 LLM 密钥,所有 embedding 与生成调用都通过 `src/providers/registry.ts` 的 provider 注册表解析。`hasUsableEnvKey()` 在每次启动时验证 env 变量是否符合 `keyFormat` 正则,并拒绝 OAuth/subscription token——这一点对记忆回写同样关键,因为 `recall` 往往需要先调用 embedding 模型。资料来源:[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)

`src/eval/run.test.ts` 与 `src/eval/run.ts` 还提供了一套评估脚本,可在不同 SANOOK_MODEL 下回放记忆检索的质量,便于在升级 embedding 模型时做回归。

---

## 常见失败模式

1. **OAuth key 污染记忆**:`hasUsableEnvKey` 会拦截 `sk-ant-oat…` 这类订阅 token,但若用户把 token 写进第二大脑的笔记里,`recall` 仍可能将其再次注入 prompt——建议使用 `redactKey()` 在回写前脱敏。
2. **Vault 路由错误**:agent 未先读 `_Index.md` 就写入,会把 `Sessions/` 的日志错放到 `Projects/`,此时需要手动通过 `Handoffs/` 流程搬运。
3. **Embedding provider 缺失**:`resolveEmbedder()` 在没有任何可用 key 时返回 `null`,`turn-retrieval` 会降级为关键词匹配,召回质量显著下降——dashboard 会显示警告徽标。

---

## See Also

- [Provider Registry & Key Policy](./providers-registry.md)
- [Dashboard Web Console](./dashboard-terminal.md)
- [Second Brain Scaffolding](./second-brain-vault.md)

---

<a id='page-extensibility'></a>

## 网关、消息通道、MCP、技能与本地面板

### 相关页面

相关主题：[项目概述、快速开始与整体架构](#page-overview), [分层记忆系统与第二大脑工作区](#page-memory)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [package.json](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)
- [src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)
- [src/dashboard/api-helpers.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/api-helpers.ts)
- [src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)
- [src/providers/models.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/models.ts)
- [src/providers/codex.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/codex.ts)
- [second-brain/README.md](https://github.com/Sir-chawakorn/sanook-cli/blob/main/second-brain/README.md)
</details>

# 网关、消息通道、MCP、技能与本地面板

sanook-cli 是一个 BYOK（Bring Your Own Key）的终端 AI 编程代理。它通过统一的"提供商注册表"将 9 家云端/本地 LLM 厂商接入同一个 agent 循环，再借由本地面板（dashboard）把这些能力暴露成 HTTP/SSE/WebSocket 端点，并允许子进程级"委托代理"（例如 codex CLI）作为另一种消息通道。本页汇总这几条通路的设计、配置与典型用法。

## 1. 架构总览

```mermaid
flowchart LR
    U[用户 / REPL] --> DASH[本地面板<br/>src/dashboard/terminal.ts]
    DASH -->|POST /api/terminal/run (SSE)| LOOP[agent loop<br/>runAgent]
    DASH -->|ws /api/terminal/shell| PTY[node-pty<br/>可选依赖]
    LOOP --> REG[提供商注册表<br/>src/providers/registry.ts]
    REG -->|sdk| SDK[Vercel AI SDK<br/>anthropic/google/openai/xai/mistral/groq]
    REG -->|delegate| CODEX[codex subprocess<br/>src/providers/codex.ts]
    SDK --> REMOTE[远端 /models 枚举<br/>src/providers/models.ts]
    REG --> SB[second-brain 记忆库<br/>second-brain/README.md]
```

上图中"网关"由 dashboard 的 HTTP/SSE 入口充当；"消息通道"由 `PROVIDERS` 表覆盖的 9 个厂商与 codex 子进程提供；"技能"与"本地面板"分别通过 `skills/` 目录（打包到 npm，见 [`package.json`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json)）和 `src/dashboard/*` 实现。资料来源：[src/dashboard/terminal.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts)、[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)。

## 2. 提供商注册表：消息通道的核心

[`src/providers/registry.ts`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts) 维护一个 `PROVIDERS` 表，每条记录声明 `envVar`、`baseURL`、`requiresKey`、`keyFormat`（用正则拒绝 OAuth/subscription 凭证）、`models`（alias → 真实 model id 的映射）以及 `create(key, baseURL)` 工厂。新增厂商只需在表内增加一项，循环、计费、密钥策略不需改动。资料来源：[src/providers/registry.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/registry.ts)。

- **`parseSpec(spec)`** 解析 `"provider:model"`、`"provider:alias"`、裸别名或裸 model id（默认 provider 为 `anthropic`）。
- **`canonicalSpec(spec)`** 在写入 REPL 状态前把别名归一为 `provider:model-id`，避免状态机里残留别名。
- **`fastSibling(spec)`** 返回同一 provider 的 `fast`/`flash`/`haiku`/`air` 兄弟型号，给 summarization、压缩等机械性任务省成本。
- **`hasUsableEnvKey(provider)`** 既要看环境变量里有没有 key，还要跑一遍 `assertDirectApiKey`，确保不是 `sk-ant-oat…` 这类被 Anthropic/Google 禁止的 OAuth 复用 token。
- **`consoleUrl(provider)`** 在错误信息/wizard 里直接给出去 console 拿 key 的 URL。

`kind: 'sdk' | 'delegate'` 字段区分"走 Vercel AI SDK"和"spawn 子进程 agent"，目前 `codex` 是唯一的 `delegate` 通道——它不查 `/models`，而是由 [`src/providers/codex.ts`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/codex.ts) 的 `codexCheck()` 验证 CLI 安装与 `codex login` 状态。

## 3. 本地面板：SSE 通道与可选 PTY

[`src/dashboard/terminal.ts`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/terminal.ts) 实现了浏览器端的两种终端后端：

- **Agent console**：`POST /api/terminal/run` 接收 prompt，直接复用 `runAgent` 并通过 SSE 转发 `text/reasoning/tool` 事件，包含 🧠 remember 事实与 ✨ 自动创建的技能——与 REPL 完全一致。多轮历史在服务端按 `sessionId` 维护，最长 20 个 session，写入前会用 `sseSend()` 检查 socket 是否已销毁。
- **Raw shell**：`ws://…/api/terminal/shell` 在 `node-pty` 与 `ws` 两个可选依赖都装好时升级为真实 PTY；否则 UI 降级。`shellStatus()` 报告当前可用性。

由于 `node-pty` 与 `ws` 在 [`package.json`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json) 中被声明为 `optionalDependencies`，缺少它们不会阻断 `npm install`，但本地面板会回退到纯 agent 通道。安装方式上，`dashboardInstall()` 暴露的 npm/npx 是当前唯一 `ready: true` 的方法，curl/irm 脚本需等 `sanook.ai` 域名与发布基础设施就绪。资料来源：[src/dashboard/api-helpers.ts](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/dashboard/api-helpers.ts)。

## 4. 远端模型枚举与 Codex 委托

[`src/providers/models.ts`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/models.ts) 的 `listRemoteModels()` 直接拉取 provider 的 `GET /models`，按厂商返回三种不同 shape：

| 厂商 | 鉴权头 | 列表字段 |
|---|---|---|
| Google | `?key=…` query | `models[].name`（带 `supportedGenerationMethods` 过滤） |
| Anthropic | `x-api-key` + `anthropic-version` | `data[].id` |
| OpenAI 兼容 | `Authorization: Bearer …` | `data[].id` |

`mergeModelOptions()` 把别名按真实 model id 分组后展平成 `ModelOption[]`，避免 React key 冲突（出现"两条选项对应同一个 model"这类 bug）。失败/超时/本地/无端点时返回 `[]`，调用方会回退到 curated alias。

[`src/providers/codex.ts`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/src/providers/codex.ts) 的 `runCodex()` 通过 stdin 投递 prompt 并解析 JSONL 事件（`text`/`usage`/`thread`），对 codex bug #15451（`--json` 在启用 tools 时被忽略）做容错。可选的 `cwd` 让父 agent 把 codex 隔离到 worktree 子目录中。

## 5. 安装、引擎与打包

[`package.json`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/package.json) 声明 `bin` 字段同时提供 `sanook` 与 `sanookai` 两条命令，都指向 `dist/bin.js`；`engines.node` 强制 `>=22`；`build` 脚本先清空 `dist/`、跑 `tsc -p tsconfig.build.json`、再执行 `scripts/copy-dashboard-static.mjs` 把静态资源复制进 dist、最后 `chmod 0o755`。发布包额外携带 `skills/`、`second-brain/`、`scripts/postinstall.mjs`、`.env.example`，让 `sanook brain` 一键 scaffold 出一个 Obsidian vault（见 [`second-brain/README.md`](https://github.com/Sir-chawakorn/sanook-cli/blob/main/second-brain/README.md)）作为跨 session 的"第二大脑"。

## 常见失败模式

- **OAuth token 被识别为直接 API key**：`ANTHROPIC_API_KEY=sk-ant-oat…` 会被 `assertDirectApiKey` 拒绝，`hasUsableEnvKey()` 返回 `false`，wizard 会强制进入而不是误判"已就绪"。
- **可选依赖缺失导致 PTY 不可用**：`shellStatus()` 会报告 `ready: false`，浏览器自动回退到 agent console；不影响主路径。
- **codex 未登录**：`codexCheck()` 返回 `{ installed: true, loggedIn: false, reason: 'ยังไม่ได้ login — รัน: codex login' }`，调度层会跳过而非崩溃。
- **Node.js 版本过低**：`<22` 会直接被 `engines` 阻拦 `npm install`。

## See Also

- [提供商与模型路由](provider-registry.md)
- [本地面板与 Web 终端](dashboard.md)
- [Second Brain 记忆库](second-brain.md)
- [Codex 委托代理](codex-delegate.md)

---

<!-- evidence_pipeline_checked: true -->

---

## Doramagic 踩坑日志

项目：Sir-chawakorn/sanook-cli

摘要：发现 7 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：配置坑 - 可能修改宿主 AI 配置。

## 1. 配置坑 · 可能修改宿主 AI 配置

- 严重度：medium
- 证据强度：source_linked
- 发现：项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主，或安装命令涉及用户配置目录。
- 对用户的影响：安装可能改变本机 AI 工具行为，用户需要知道写入位置和回滚方法。
- 证据：capability.host_targets | https://github.com/Sir-chawakorn/sanook-cli | host_targets=mcp_host, claude, claude_code, gemini_cli, codex

## 2. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 证据：capability.assumptions | https://github.com/Sir-chawakorn/sanook-cli | README/documentation is current enough for a first validation pass.

## 3. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 证据：evidence.maintainer_signals | https://github.com/Sir-chawakorn/sanook-cli | last_activity_observed missing

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 证据：downstream_validation.risk_items | https://github.com/Sir-chawakorn/sanook-cli | no_demo; severity=medium

## 5. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 证据：risks.scoring_risks | https://github.com/Sir-chawakorn/sanook-cli | no_demo; severity=medium

## 6. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 证据：evidence.maintainer_signals | https://github.com/Sir-chawakorn/sanook-cli | issue_or_pr_quality=unknown

## 7. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 证据：evidence.maintainer_signals | https://github.com/Sir-chawakorn/sanook-cli | release_recency=unknown

<!-- canonical_name: Sir-chawakorn/sanook-cli; human_manual_source: deepwiki_human_wiki -->
