AtomCode 源码编译与二次开发入门
引言
AtomCode 作为一款开源的 AI 编码助手,不仅提供了强大的功能,还开放了全部源代码,允许开发者进行二次开发和定制。本文将详细介绍如何从源码编译 AtomCode,如何理解其架构设计,以及如何进行二次开发,帮助你打造属于自己的 AI 编码工具。
一、环境准备
1.1 系统要求
| 项目 | 要求 |
|---|---|
| 操作系统 | Windows 10+/macOS 12+/Linux |
| Node.js | ≥ 18.0.0 |
| npm | ≥ 9.0.0 |
| Git | ≥ 2.0.0 |
| Rust | ≥ 1.70.0(可选,用于原生模块) |
1.2 安装依赖
bash
# 安装 Node.js 和 npm
# 访问 https://nodejs.org/ 下载
# 安装 Git
# 访问 https://git-scm.com/ 下载
# 安装 Rust(可选)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 验证安装
node --version
npm --version
git --version
二、获取源码
2.1 克隆仓库
bash
# 克隆主仓库
git clone https://atomgit.com/atomcode/atomcode.git
# 进入项目目录
cd atomcode
# 查看目录结构
ls -la
2.2 项目结构
atomcode/
├── .github/ # GitHub 配置文件
├── apps/ # 应用程序目录
│ ├── desktop/ # 桌面端应用
│ ├── web/ # Web 端应用
│ └── mobile/ # 移动端应用(开发中)
├── packages/ # 核心包目录
│ ├── core/ # 核心功能
│ ├── ai/ # AI 模块
│ ├── editor/ # 编辑器模块
│ ├── plugins/ # 插件系统
│ └── utils/ # 工具函数
├── docs/ # 文档
├── scripts/ # 脚本文件
├── package.json # 根 package.json
├── tsconfig.json # TypeScript 配置
└── README.md # 项目说明
2.3 安装依赖
bash
# 安装项目依赖
npm install
# 如果使用 pnpm(推荐)
pnpm install
# 安装子包依赖
pnpm install --recursive
三、理解架构设计
3.1 核心架构
┌─────────────────────────────────────────────────────────────┐
│ 应用层 (Apps) │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Desktop │ │ Web │ │ Mobile │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
└────────┼──────────────┼──────────────┼────────────────────┘
│ │ │
┌────────▼──────────────────────────────▼────────────────────┐
│ 核心层 (Packages) │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Core │ │ AI │ │ Editor │ │
│ │ 核心功能 │ │ AI 模块 │ │ 编辑器模块 │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ ┌───────────┐ ┌───────────┐ │
│ │ Plugins │ │ Utils │ │
│ │ 插件系统 │ │ 工具函数 │ │
│ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │ │
┌────────▼──────────────────────────────▼────────────────────┐
│ 基础设施层 │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Node.js │ │ Rust │ │ Electron │ │
│ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────┘
3.2 核心模块详解
Core 模块:
typescript
// packages/core/src/index.ts
export interface CoreConfig {
version: string;
appName: string;
defaultConfig: Config;
}
export class Core {
private config: CoreConfig;
private modules: Map<string, Module> = new Map();
constructor(config: CoreConfig) {
this.config = config;
}
registerModule(module: Module) {
this.modules.set(module.id, module);
}
getModule<T extends Module>(id: string): T | undefined {
return this.modules.get(id) as T;
}
async init() {
for (const module of this.modules.values()) {
await module.init();
}
}
}
AI 模块:
typescript
// packages/ai/src/index.ts
export interface AIModel {
id: string;
name: string;
provider: string;
apiKey: string;
baseUrl: string;
maxTokens: number;
temperature: number;
}
export class AIService {
private models: Map<string, AIModel> = new Map();
private currentModel: AIModel | null = null;
registerModel(model: AIModel) {
this.models.set(model.id, model);
}
setCurrentModel(modelId: string): boolean {
const model = this.models.get(modelId);
if (model) {
this.currentModel = model;
return true;
}
return false;
}
async generate(prompt: string): Promise<string> {
if (!this.currentModel) {
throw new Error('No model selected');
}
return this.callAPI(this.currentModel, prompt);
}
}
Editor 模块:
typescript
// packages/editor/src/index.ts
export interface EditorConfig {
theme: string;
fontSize: number;
tabSize: number;
lineNumbers: boolean;
}
export class Editor {
private config: EditorConfig;
private view: EditorView;
constructor(config: EditorConfig) {
this.config = config;
this.view = new EditorView(config);
}
setContent(content: string) {
this.view.setContent(content);
}
getContent(): string {
return this.view.getContent();
}
highlightSyntax(language: string) {
this.view.highlight(language);
}
}
3.3 插件系统架构
typescript
// packages/plugins/src/index.ts
export interface PluginManifest {
id: string;
name: string;
version: string;
description: string;
author: string;
dependencies?: string[];
entry: string;
}
export interface PluginAPI {
core: Core;
ai: AIService;
editor: Editor;
registerCommand(command: Command): void;
registerHook(hook: Hook): void;
}
export class PluginLoader {
private plugins: Map<string, Plugin> = new Map();
private api: PluginAPI;
constructor(api: PluginAPI) {
this.api = api;
}
async loadPlugin(manifest: PluginManifest): Promise<void> {
const module = await import(manifest.entry);
const plugin = new module.default(this.api);
this.plugins.set(manifest.id, plugin);
await plugin.init();
}
async unloadPlugin(pluginId: string): Promise<void> {
const plugin = this.plugins.get(pluginId);
if (plugin) {
await plugin.destroy();
this.plugins.delete(pluginId);
}
}
}
四、编译与运行
4.1 编译项目
bash
# 编译所有包
pnpm build
# 编译特定包
pnpm build --filter @atomcode/core
pnpm build --filter @atomcode/ai
pnpm build --filter @atomcode/editor
# 编译桌面应用
pnpm build --filter atomcode-desktop
# 编译 Web 应用
pnpm build --filter atomcode-web
4.2 运行开发服务器
bash
# 运行桌面应用开发模式
pnpm dev --filter atomcode-desktop
# 运行 Web 应用开发模式
pnpm dev --filter atomcode-web
4.3 构建生产版本
bash
# 构建桌面应用
pnpm build:prod --filter atomcode-desktop
# 构建 Web 应用
pnpm build:prod --filter atomcode-web
# 构建安装包(桌面应用)
pnpm package --filter atomcode-desktop
五、二次开发实战
5.1 创建自定义插件
步骤 1:创建插件目录
bash
mkdir -p packages/plugins/my-plugin
cd packages/plugins/my-plugin
步骤 2:创建 package.json
json
{
"name": "@atomcode/my-plugin",
"version": "1.0.0",
"description": "My custom AtomCode plugin",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
},
"dependencies": {
"@atomcode/core": "^1.0.0",
"@atomcode/ai": "^1.0.0",
"@atomcode/editor": "^1.0.0"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
步骤 3:创建插件代码
typescript
// src/index.ts
import { PluginAPI, Command, Hook } from '@atomcode/core';
export default class MyPlugin {
private api: PluginAPI;
constructor(api: PluginAPI) {
this.api = api;
}
async init() {
console.log('MyPlugin initialized');
// 注册命令
this.registerCommands();
// 注册钩子
this.registerHooks();
}
private registerCommands() {
const helloCommand: Command = {
id: 'my-plugin.hello',
name: 'Hello World',
description: 'Say hello to the world',
shortcut: 'Ctrl+Shift+H',
execute: () => {
this.api.editor.setContent('Hello from MyPlugin!');
}
};
this.api.registerCommand(helloCommand);
}
private registerHooks() {
const fileOpenHook: Hook = {
id: 'my-plugin.file-open',
type: 'file-open',
callback: (file) => {
console.log(`File opened: ${file.name}`);
}
};
this.api.registerHook(fileOpenHook);
}
async destroy() {
console.log('MyPlugin destroyed');
}
}
步骤 4:编译插件
bash
pnpm install
pnpm build
步骤 5:注册插件
在 packages/plugins/src/plugins.ts 中注册你的插件:
typescript
import MyPlugin from '@atomcode/my-plugin';
export const plugins = [
// 其他插件...
{
id: 'my-plugin',
name: 'My Plugin',
version: '1.0.0',
description: 'My custom AtomCode plugin',
entry: '@atomcode/my-plugin',
Plugin: MyPlugin
}
];
5.2 扩展 AI 模块
添加自定义模型:
typescript
// packages/ai/src/models/custom.ts
import { AIModel } from '../types';
import { BaseModel } from './base';
export class CustomModel extends BaseModel {
constructor(config: AIModel) {
super(config);
}
async generate(prompt: string): Promise<string> {
// 实现自定义模型的调用逻辑
const response = await fetch(this.config.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiKey}`
},
body: JSON.stringify({
prompt,
max_tokens: this.config.maxTokens,
temperature: this.config.temperature
})
});
const data = await response.json();
return data.choices[0].text;
}
}
注册自定义模型:
typescript
// packages/ai/src/index.ts
import { CustomModel } from './models/custom';
const aiService = new AIService();
aiService.registerModel({
id: 'custom-model',
name: 'Custom Model',
provider: 'custom',
apiKey: process.env.CUSTOM_API_KEY || '',
baseUrl: 'https://api.custom-model.com/v1/completions',
maxTokens: 4096,
temperature: 0.7
});
aiService.setCurrentModel('custom-model');
5.3 自定义编辑器主题
创建主题文件:
typescript
// packages/editor/src/themes/my-theme.ts
import { Theme } from '../types';
export const myTheme: Theme = {
id: 'my-theme',
name: 'My Theme',
type: 'dark',
colors: {
background: '#1a1a2e',
foreground: '#e4e4e7',
primary: '#818cf8',
secondary: '#c084fc',
success: '#4ade80',
warning: '#fbbf24',
error: '#f87171',
comment: '#6b7280',
string: '#a78bfa',
number: '#fbbf24',
keyword: '#f472b6',
function: '#60a5fa',
variable: '#e4e4e7',
class: '#34d399',
interface: '#2dd4bf',
type: '#a5b4fc'
},
font: {
family: 'JetBrains Mono, Consolas, monospace',
size: 14
}
};
注册主题:
typescript
// packages/editor/src/index.ts
import { myTheme } from './themes/my-theme';
const editor = new Editor({
theme: 'my-theme',
fontSize: 14,
tabSize: 2,
lineNumbers: true
});
editor.registerTheme(myTheme);
六、调试与测试
6.1 调试技巧
bash
# 使用 Chrome DevTools 调试桌面应用
pnpm dev --filter atomcode-desktop --inspect
# 使用 VS Code 调试
# 配置 .vscode/launch.json
# 查看日志
pnpm dev --filter atomcode-desktop --verbose
VS Code 调试配置:
json
{
"version": "0.2.0",
"configurations": [
{
"name": "AtomCode Desktop",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/apps/desktop",
"program": "${workspaceFolder}/apps/desktop/src/main.ts",
"runtimeArgs": ["--inspect"],
"preLaunchTask": "npm: build"
},
{
"name": "AtomCode Web",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/apps/web"
}
]
}
6.2 测试框架
bash
# 运行所有测试
pnpm test
# 运行特定包的测试
pnpm test --filter @atomcode/core
pnpm test --filter @atomcode/ai
# 运行测试并生成覆盖率报告
pnpm test --coverage
测试示例:
typescript
// packages/ai/src/tests/ai-service.test.ts
import { AIService, AIModel } from '../index';
describe('AIService', () => {
let aiService: AIService;
beforeEach(() => {
aiService = new AIService();
});
describe('registerModel', () => {
it('should register a model', () => {
const model: AIModel = {
id: 'test-model',
name: 'Test Model',
provider: 'test',
apiKey: 'test-key',
baseUrl: 'http://localhost:3000',
maxTokens: 1024,
temperature: 0.7
};
aiService.registerModel(model);
expect(aiService.getModel('test-model')).toEqual(model);
});
});
describe('setCurrentModel', () => {
it('should set the current model', () => {
const model: AIModel = {
id: 'test-model',
name: 'Test Model',
provider: 'test',
apiKey: 'test-key',
baseUrl: 'http://localhost:3000',
maxTokens: 1024,
temperature: 0.7
};
aiService.registerModel(model);
const result = aiService.setCurrentModel('test-model');
expect(result).toBe(true);
});
});
});
七、贡献代码
7.1 贡献流程
┌─────────────────────────────────────────────────────────────┐
│ 贡献流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. Fork 仓库 │
│ └── 在 AtomGit 上 Fork atomcode/atomcode │
├─────────────────────────────────────────────────────────────┤
│ 2. 克隆仓库 │
│ └── git clone https://atomgit.com/your-username/atomcode│
├─────────────────────────────────────────────────────────────┤
│ 3. 创建分支 │
│ └── git checkout -b feature/my-feature │
├─────────────────────────────────────────────────────────────┤
│ 4. 编写代码 │
│ └── 实现功能,添加测试 │
├─────────────────────────────────────────────────────────────┤
│ 5. 提交代码 │
│ └── git commit -m "feat: add my feature" │
├─────────────────────────────────────────────────────────────┤
│ 6. 推送到远程 │
│ └── git push origin feature/my-feature │
├─────────────────────────────────────────────────────────────┤
│ 7. 创建 Pull Request │
│ └── 在 AtomGit 上创建 PR │
└─────────────────────────────────────────────────────────────┘
7.2 代码规范
TypeScript 代码规范:
typescript
// ✅ 好的代码风格
export class AIService {
private models: Map<string, AIModel> = new Map();
registerModel(model: AIModel): void {
this.models.set(model.id, model);
}
getModel(id: string): AIModel | undefined {
return this.models.get(id);
}
}
// ❌ 不好的代码风格
export class AIService {
private models: Map<string, AIModel> = new Map();
registerModel(model: AIModel) {
this.models.set(model.id, model);
}
getModel(id: string) {
return this.models.get(id);
}
}
提交信息规范:
bash
# 格式:<type>(<scope>): <description>
# 示例:
git commit -m "feat(ai): add custom model support"
git commit -m "fix(editor): resolve syntax highlighting issue"
git commit -m "docs(core): update API documentation"
git commit -m "refactor(plugins): simplify plugin loader"
git commit -m "test(ai): add unit tests for AI service"
7.3 常见贡献类型
| 类型 | 说明 | 示例 |
|---|---|---|
| Bug Fix | 修复 Bug | 修复编辑器崩溃问题 |
| Feature | 添加新功能 | 添加代码解释功能 |
| Enhancement | 改进现有功能 | 优化代码补全速度 |
| Documentation | 更新文档 | 添加 API 参考文档 |
| Refactor | 重构代码 | 简化核心模块结构 |
| Test | 添加测试 | 添加单元测试用例 |
| Plugin | 创建插件 | 创建代码质量插件 |
八、部署与发布
8.1 打包应用
bash
# 打包桌面应用
pnpm package --filter atomcode-desktop
# 打包 Web 应用
pnpm build:prod --filter atomcode-web
打包产物:
apps/desktop/dist/
├── win-unpacked/ # Windows 解压版
├── linux-unpacked/ # Linux 解压版
├── mac-unpacked/ # macOS 解压版
├── atomcode-setup.exe # Windows 安装包
├── atomcode.deb # Debian/Ubuntu 安装包
└── atomcode.dmg # macOS 安装包
8.2 发布新版本
更新版本号:
bash
# 更新根 package.json 版本号
pnpm version minor
# 更新所有子包版本号
pnpm version --recursive minor
发布到 npm:
bash
# 登录 npm
npm login
# 发布所有包
pnpm publish --recursive
发布到 GitHub Releases:
bash
# 创建发布标签
git tag v1.0.0
git push origin v1.0.0
# 上传发布包
# 在 GitHub Releases 页面手动上传
九、常见问题与解决方案
Q1:编译失败
原因:依赖版本不兼容或缺少依赖
解决方案:
bash
# 删除 node_modules 并重新安装
rm -rf node_modules
pnpm install
# 清理构建缓存
pnpm clean
pnpm build
# 检查 Node.js 版本
node --version
Q2:运行时错误
原因:模块未正确编译或配置错误
解决方案:
bash
# 检查日志
pnpm dev --verbose
# 重新编译项目
pnpm build
# 检查配置文件
cat ~/.atomcode/config.json
Q3:插件加载失败
原因:插件依赖缺失或代码错误
解决方案:
bash
# 检查插件依赖
cd packages/plugins/my-plugin
pnpm install
# 编译插件
pnpm build
# 检查插件日志
cat ~/.atomcode/logs/plugin.log
Q4:AI 模型调用失败
原因:API Key 无效或网络问题
解决方案:
bash
# 检查 API Key
echo $OPENAI_API_KEY
# 测试网络连接
curl https://api.openai.com/v1/models
# 检查模型配置
cat ~/.atomcode/models.json
十、未来发展方向
10.1 技术路线图
- 短期目标:完善核心功能,优化性能
- 中期目标:支持更多 AI 模型,扩展插件生态
- 长期目标:构建完整的 AI 编码平台
10.2 社区发展
- 建立官方文档网站
- 组织社区活动和 Hackathon
- 建立贡献者激励机制
- 支持多语言社区
10.3 商业化方向
- 提供企业版定制服务
- 开发付费插件市场
- 提供云端 AI 服务
- 建立技术支持体系
结语
AtomCode 的开源特性为开发者提供了无限的定制空间。通过源码编译和二次开发,你可以打造一个完全符合自己需求的 AI 编码工具。无论是创建插件、扩展 AI 模块,还是自定义编辑器主题,AtomCode 的架构设计都提供了良好的扩展性。
二次开发建议:
- 📚 先理解项目架构,再进行修改
- 🔧 从创建插件开始,逐步深入核心模块
- ✅ 编写测试用例,确保代码质量
- 🤝 积极参与社区,分享你的成果
现在就开始探索 AtomCode 的源码,打造属于你的 AI 编码工具吧!
本文为原创内容,基于作者实际编译和二次开发经验整理。如需转载,请注明出处。