8.8 KiB
8.8 KiB
codespace 可运行后端骨架设计
日期:2026-07-02
目标
在当前目录 /Users/taochen/llm/codespace 生成一个 Go 后端项目骨架。第一版目标是可运行、可测试、边界清晰的 Workspace Service,而不是完整 Web IDE。
核心能力:
- 每个 workspace 对应本地文件系统中的一个目录。
- 文件读写统一通过
FileSystem接口,第一版实现LocalFS。 - OpenCode 以本机进程方式启动,工作目录
cwd指向 workspace root。 - HTTP handler 不直接操作文件系统或进程,必须经过 service 层。
- 预留 watcher、Git、LSP、AI Agent 的后续扩展点。
- 不依赖 Docker。
非目标
第一版不实现:
- 用户认证和权限系统。
- Docker / 容器隔离。
- Git 集成。
- LSP。
- AI Agent 编排。
- Web 前端。
- 完整 watcher / WebSocket 文件事件推送。
这些能力以后接入,但不能污染第一版的核心边界。
目录结构
.
├── cmd/
│ └── server/
│ └── main.go
├── configs/
│ └── config.yaml
├── internal/
│ ├── api/
│ │ ├── router.go
│ │ ├── workspace_handler.go
│ │ ├── file_handler.go
│ │ └── process_handler.go
│ ├── workspace/
│ │ ├── manager.go
│ │ ├── workspace.go
│ │ └── path.go
│ ├── fs/
│ │ ├── filesystem.go
│ │ ├── localfs.go
│ │ └── fileinfo.go
│ ├── process/
│ │ ├── manager.go
│ │ ├── session.go
│ │ └── opencode.go
│ ├── watcher/
│ │ └── watcher.go
│ ├── service/
│ │ ├── workspace_service.go
│ │ ├── file_service.go
│ │ └── process_service.go
│ ├── model/
│ │ ├── workspace.go
│ │ ├── file.go
│ │ └── process.go
│ └── util/
│ ├── path.go
│ └── errors.go
├── pkg/
│ ├── config/
│ ├── logger/
│ └── response/
├── web/
├── docs/
├── Makefile
├── go.mod
└── README.md
架构
HTTP request
↓
internal/api handler
↓
internal/service
↓
workspace.Manager / fs.FileSystem / process.Manager
↓
local workspace directory + opencode process
边界规则:
api只负责 HTTP 编解码、路由参数、状态码。service负责业务编排和校验。workspace负责 workspace 元数据和 root 目录管理。fs负责 workspace 内文件操作。process负责进程生命周期。watcher第一版只保留接口/占位,不接入运行链路。
核心数据结构
Workspace
type Workspace struct {
ID string `json:"id"`
Root string `json:"root"`
}
含义:
ID是外部 API 使用的 workspace 标识。Root是服务端本地绝对路径。- 第一版不引入数据库,workspace 是否存在由目录和 manager 判断。
FileInfo
type FileInfo struct {
Name string `json:"name"`
Path string `json:"path"`
IsDir bool `json:"isDir"`
Size int64 `json:"size"`
ModTime time.Time `json:"modTime"`
}
Path 始终是 workspace 内相对路径,不向 API 暴露宿主机绝对路径。
ProcessStatus
type ProcessStatus struct {
WorkspaceID string `json:"workspaceId"`
Running bool `json:"running"`
PID int `json:"pid,omitempty"`
}
Workspace Manager
职责:
- 创建 workspace 目录。
- 查询 workspace。
- 删除 workspace 目录。
- 生成 workspace root 路径。
接口:
type Manager interface {
Create(id string) (*Workspace, error)
Get(id string) (*Workspace, error)
Delete(id string) error
}
路径策略:
- 配置项
workspaceRoot默认使用./workspaces。 - workspace root 为
<workspaceRoot>/<workspaceID>。 - workspace ID 第一版限制为安全路径片段,只允许字母、数字、
-、_、.。
FileSystem
所有文件操作必须走接口:
type FileSystem interface {
List(path string) ([]FileInfo, error)
Read(path string) ([]byte, error)
Write(path string, data []byte) error
Mkdir(path string) error
Remove(path string) error
Rename(oldPath, newPath string) error
Stat(path string) (*FileInfo, error)
}
第一版实现 LocalFS:
LocalFS绑定一个 workspace root。- 所有传入路径都是 workspace 内相对路径。
- 解析路径时必须阻止
../逃逸。 - API 不返回宿主机绝对路径。
Process Manager
职责:按 workspace 管理 OpenCode 进程。
接口:
type Manager interface {
Start(workspaceID string, workspaceRoot string) error
Stop(workspaceID string) error
Restart(workspaceID string, workspaceRoot string) error
Status(workspaceID string) Status
}
第一版行为:
- 命令默认是
opencode,通过配置项opencodeCommand可改。 cmd.Dir = workspace.Root。HOME默认设置为 workspace root,避免不同 workspace 共享 CLI 状态。- 同一个 workspace 已有运行进程时,再次 start 返回已运行错误,不重复启动。
- stop 使用进程 kill;优雅退出可以后续补。
Service 层
WorkspaceService
CreateWorkspace(id string)GetWorkspace(id string)DeleteWorkspace(id string)
删除 workspace 前,先停止对应进程,避免目录被运行进程占用。
FileService
List(workspaceID, path string)Read(workspaceID, path string)Write(workspaceID, path string, data []byte)Mkdir(workspaceID, path string)Remove(workspaceID, path string)Rename(workspaceID, oldPath, newPath string)Stat(workspaceID, path string)
每次通过 workspace manager 取 workspace,再构造绑定该 root 的 LocalFS。
ProcessService
Start(workspaceID string)Stop(workspaceID string)Restart(workspaceID string)Status(workspaceID string)
ProcessService 不自己拼路径,只通过 workspace manager 取 root。
HTTP API
基础路径:/api
健康检查
GET /healthz
返回:
{"status":"ok"}
Workspace
POST /api/workspaces
GET /api/workspaces/{id}
DELETE /api/workspaces/{id}
创建请求:
{"id":"user1"}
Files
GET /api/workspaces/{id}/files?path=.
GET /api/workspaces/{id}/files/read?path=main.go
PUT /api/workspaces/{id}/files/write
POST /api/workspaces/{id}/files/mkdir
DELETE /api/workspaces/{id}/files?path=main.go
POST /api/workspaces/{id}/files/rename
写文件请求:
{"path":"main.go","content":"package main\n"}
重命名请求:
{"oldPath":"old.go","newPath":"new.go"}
Process
POST /api/workspaces/{id}/process/start
POST /api/workspaces/{id}/process/stop
POST /api/workspaces/{id}/process/restart
GET /api/workspaces/{id}/process/status
配置
configs/config.yaml:
server:
addr: ":8080"
workspace:
root: "./workspaces"
process:
opencodeCommand: "opencode"
配置加载规则:
- 先加载默认值。
- 如果配置文件存在,覆盖默认值。
- 环境变量可覆盖:
CODESPACE_ADDRCODESPACE_WORKSPACE_ROOTCODESPACE_OPENCODE_COMMAND
错误处理
统一 JSON 响应:
{
"error": {
"code": "bad_request",
"message": "invalid workspace id"
}
}
常见映射:
- 参数错误:400
- workspace 不存在:404
- 文件不存在:404
- 进程已运行:409
- 内部错误:500
第一版只做简单错误类型,不引入复杂错误框架。
测试策略
最小测试:
- workspace ID 校验和路径生成。
- LocalFS 路径逃逸防护。
- LocalFS 基础读写/列表。
- Process manager 状态逻辑使用可配置命令测试,不依赖真实 OpenCode。
- HTTP handler 可做轻量测试,至少覆盖 health 和 workspace 创建。
验收命令:
go test ./...
go run ./cmd/server
兼容性与风险
- 当前目录是项目根目录,不再额外创建
codespace/子目录。 - 初始化会创建标准 Go 项目文件;如果目标文件已存在,实施阶段必须先检查,避免覆盖用户已有内容。
- 不改变公共 API,因为这是初始版本。
- 最大安全风险是路径逃逸,必须在
LocalFS和 workspace ID 校验中处理。 - 最大复杂度风险是过早引入 watcher/Git/LSP/Agent;第一版明确不实现。
骨架完成标准
完成后应满足:
- 当前目录包含完整 Go module。
go test ./...通过。go run ./cmd/server能启动 HTTP 服务。- 可以创建 workspace、读写 workspace 内文件、查询/启动/停止进程。
- Handler 不直接调用
os.*或exec.*。 - 文件路径不能逃逸 workspace root。