Skip to main content

OpenCode 架构文档

1. 项目概述

OpenCode是一个开源的AI编码代理工具,旨在提供强大的AI辅助开发体验。它采用模块化架构设计,支持多种交互界面(终端TUI、Web应用、桌面应用)和模型提供商。

核心特性

  • 多界面支持:终端TUI、Web应用、桌面应用
  • 客户端-服务器架构
  • 多模型提供商兼容(Claude、OpenAI、Google等)
  • 内置代理系统(build和plan)
  • LSP支持
  • 会话管理
  • 主题定制

2. 目录结构

OpenCode采用monorepo结构,使用Bun作为包管理器:
opencode/
├── .github/           # GitHub配置和工作流
├── .opencode/         # 内置工具和主题配置
├── github/            # GitHub Action集成
├── infra/             # 基础设施配置
├── nix/               # Nix构建配置
├── packages/          # 核心包目录
│   ├── app/           # Web应用前端
│   ├── console/       # 控制台应用(前端+后端)
│   ├── desktop/       # 桌面应用
│   ├── docs/          # 文档
│   └── opencode/      # 主包(核心功能+CLI)
└── package.json       # 根包配置

3. 核心模块

3.1 opencode 包

主包,包含核心功能和CLI,位于packages/opencode/

主要组件

  • CLI: 命令行界面,支持多种命令
  • Agent: AI代理系统,包含build和plan两种内置代理
  • LSP: 语言服务器协议支持
  • File: 文件系统操作和观察器
  • Auth: 认证系统
  • Config: 配置管理
  • Bus: 事件总线
  • TUI: 终端用户界面(基于SolidJS)

核心入口

packages/opencode/src/index.ts是主入口文件,负责解析命令行参数并执行相应命令。

3.2 app 包

Web应用前端,位于packages/app/,采用SolidJS框架构建,实现了与OpenCode服务器的交互和用户界面:

主要组件

  • Components: UI组件(文件树、终端、标题栏等)
  • Context: 应用上下文管理(Sync上下文、会话管理等)
  • Pages: 页面组件(首页、会话页、错误页等)
  • Utils: 工具函数

核心架构

app包采用组件化架构,使用SolidJS的响应式系统和上下文管理:
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   UI组件层      │     │   上下文层      │     │   SDK层         │
│                 │     │                 │     │                 │
│ ┌─────────────┐ │     │ ┌─────────────┐ │     │ ┌─────────────┐ │
│ │   会话界面   │ │     │ │   Sync上下文 │ │     │ │  OpenCode   │ │
│ └─────────────┘ │     │ └─────────────┘ │     │ │    SDK      │ │
│ ┌─────────────┐ │     │ ┌─────────────┐ │     │ └─────────────┘ │
│ │  消息展示   │ │     │ │  会话管理   │ │     │                 │
│ └─────────────┘ │     │ └─────────────┘ │     └─────────────────┘
│ ┌─────────────┐ │     │ ┌─────────────┐ │
│ │   输入框    │ │     │ │  状态存储   │ │
│ └─────────────┘ │     │ └─────────────┘ │
└─────────────────┘     └─────────────────┘

状态管理

使用SolidJS的Store API和Context API实现状态管理:
  • Sync上下文: 管理消息同步和存储
  • 会话存储: 管理当前会话状态
  • 主题存储: 管理UI主题设置

消息存储实现

前端存储
消息存储使用SolidJS的Store API实现,位于packages/app/src/context/sync.tsx
// 核心存储结构
const store = createStore<{
  message: Record<string, Message[]>
  part: Record<string, Part[]>
}>({ message: {}, part: {} })

// 加载历史消息
async loadMessages(sessionID: string, limit: number) {
  // 调用SDK获取历史消息
  const result = await sdk.client.session.messages({
    sessionID,
    limit,
  })
  // 更新本地存储
  batch(() => {
    setStore("message", sessionID, reconcile(next, { key: "id" }))
    // 更新消息部分
    for (const message of items) {
      setStore("part", message.info.id, reconcile(...))
    }
  })
}
后端存储
后端消息存储位于packages/opencode/src/storage/storage.ts,使用文件系统存储:
// 存储路径结构
const dir = path.join(Global.Path.data, "storage") // ~/.local/share/opencode/storage

// 存储操作
await Storage.write(["session", project.id, id], result)
await Storage.read(["message", sessionID, messageID])
历史消息拉取接口
服务端接口位于packages/opencode/src/server/routes/session.ts
// 接口定义
.get("/:sessionID/message", describeRoute({
  summary: "Get session messages",
  description: "Retrieve all messages in a session, including user prompts and AI responses.",
  operationId: "session.messages",
  responses: {
    200: {
      description: "List of messages",
      content: {
        "application/json": {
          schema: resolver(MessageV2.WithParts.array()),
        },
      },
    },
    ...errors(400, 404),
  },
}), async (c) => {
  const query = c.req.valid("query")
  const messages = await Session.messages({
    sessionID: c.req.valid("param").sessionID,
    limit: query.limit,
  })
  return c.json(messages)
})

模型流式回复上屏实现

核心组件
  • Typewriter组件: 实现打字机效果,位于packages/ui/src/components/typewriter.tsx
  • SessionTurn组件: 渲染会话轮次,位于packages/ui/src/components/session-turn.tsx
  • Message组件: 处理消息显示,位于packages/ui/src/components/message.tsx
实现流程
  1. 接收流式响应: 客户端通过SDK接收服务器的流式响应
  2. 分块处理: 逐块接收消息内容
  3. 打字机效果: 使用Typewriter组件实现渐进式渲染
  4. 自动滚动: 使用createAutoScroll钩子实现自动滚动到底部
// Typewriter组件核心实现
const Typewriter = <T extends ValidComponent = "p">(props: { text?: string; class?: string; as?: T }) => {
  const [store, setStore] = createStore({
    typing: false,
    displayed: "",
    cursor: true,
  })

  createEffect(() => {
    const text = props.text
    if (!text) return

    // 打字机效果实现
    let i = 0
    const timeouts: ReturnType<typeof setTimeout>[] = []
    setStore("typing", true)
    setStore("displayed", "")
    setStore("cursor", true)

    const getTypingDelay = () => {
      // 随机延迟,模拟真实打字效果
      const random = Math.random()
      if (random < 0.05) return 150 + Math.random() * 100
      if (random < 0.15) return 80 + Math.random() * 60
      return 30 + Math.random() * 50
    }

    const type = () => {
      if (i < text.length) {
        setStore("displayed", text.slice(0, i + 1))
        i++
        timeouts.push(setTimeout(type, getTypingDelay()))
      } else {
        setStore("typing", false)
        timeouts.push(setTimeout(() => setStore("cursor", false), 2000))
      }
    }

    timeouts.push(setTimeout(type, 200))

    onCleanup(() => {
      for (const timeout of timeouts) clearTimeout(timeout)
    })
  })

  return (
    <Dynamic component={props.as || "p"} class={props.class}>
      {store.displayed}
      <Show when={store.cursor}>
        <span classList={{ "blinking-cursor": !store.typing }}></span>
      </Show>
    </Dynamic>
  )
}

自动滚动机制

使用createAutoScroll钩子实现消息区域的自动滚动:
// createAutoScroll钩子
const useAutoScroll = (ref: () => HTMLElement | undefined, enabled: Accessor<boolean>) => {
  let timeoutId: number

  createEffect(() => {
    const element = ref()
    if (!element || !enabled()) return

    const checkScroll = () => {
      const { scrollTop, scrollHeight, clientHeight } = element
      const isAtBottom = scrollHeight - scrollTop - clientHeight < 100

      if (isAtBottom) {
        timeoutId = window.setTimeout(() => {
          element.scrollTop = scrollHeight
        }, 10)
      }
    }

    const observer = new MutationObserver(checkScroll)
    observer.observe(element, { childList: true, subtree: true })

    checkScroll()

    onCleanup(() => {
      observer.disconnect()
      clearTimeout(timeoutId)
    })
  })
}

3.3 console 包

控制台应用,位于packages/console/

主要组件

  • app: 控制台前端应用
  • core: 控制台后端核心
  • function: 云函数
  • mail: 邮件服务
  • resource: 资源管理

3.4 desktop 包

桌面应用,位于packages/desktop/

主要组件

  • src: 桌面应用源代码
  • src-tauri: Tauri配置和Rust代码

4. 架构特点

4.1 客户端-服务器架构

OpenCode采用客户端-服务器架构,允许在不同设备上运行客户端,同时保持核心功能在服务器端:
┌─────────────┐     ┌─────────────┐
│  终端TUI    │     │   Web应用   │
└──────┬──────┘     └──────┬──────┘
       │                   │
       └───────────┬───────┘

            ┌─────────────┐
            │  OpenCode   │
            │    服务器   │
            └──────┬──────┘


            ┌─────────────┐
            │  AI模型API  │
            └─────────────┘

4.2 代理系统

OpenCode内置两种代理:
  • build代理: 默认代理,具有完整访问权限,用于开发工作
  • plan代理: 只读代理,用于分析和代码探索
    • 默认拒绝文件编辑
    • 运行bash命令前需询问权限
    • 适合探索不熟悉的代码库或规划更改

4.3 多模型支持

OpenCode支持多种AI模型提供商,包括Claude、OpenAI、Google等,用户可以根据需要进行配置。

5. 技术栈

前端技术

  • SolidJS: UI框架(用于终端TUI和Web应用)
  • Vite: 构建工具
  • TailwindCSS: CSS框架
  • Solid Router: 路由管理

后端技术

  • TypeScript: 主要开发语言
  • Bun: 运行时和包管理器
  • Hono: Web框架
  • Drizzle: ORM

桌面技术

  • Tauri: 桌面应用框架
  • Rust: 系统级开发

6. 关键功能模块

6.1 CLI命令系统

OpenCode提供丰富的CLI命令,包括:
  • run: 运行代理
  • generate: 生成代码
  • auth: 认证管理
  • agent: 代理管理
  • models: 模型管理
  • serve: 启动服务器
  • web: 启动Web应用
  • session: 会话管理

6.2 文件系统操作

文件系统模块提供:
  • 文件读写
  • 目录遍历
  • 文件观察器
  • 忽略规则支持

6.3 会话管理

会话管理模块允许用户:
  • 创建和管理会话
  • 重命名会话
  • 导出和导入会话

6.4 主题系统

OpenCode支持多种内置主题,并允许用户自定义主题。

7. 开发和部署

7.1 开发环境

  • Bun: 用于开发和构建
  • Turbo: 用于类型检查
  • Husky: 用于Git钩子

7.2 部署选项

OpenCode支持多种部署方式:
  • 本地安装
  • 桌面应用
  • 云部署

8. 未来发展方向

  • 增强多代理协作能力
  • 扩展模型支持
  • 改进性能和响应速度
  • 增加更多开发工具集成

9. 总结

OpenCode采用模块化、客户端-服务器架构设计,提供多种交互界面和模型支持,旨在为开发者提供强大的AI辅助开发体验。其核心优势在于灵活性、可扩展性和多平台支持。