Apex AI 辅助编码助手的设计和实践|得物技术
在谈安全保障之前,首先必须明确 “网页应用(web application)” 是什么。智能手机上的应用可以看作一个压缩包 (zip);网页则由相互关联的资源组成 ——HTML、JavaScript、WASM、CSS 等,这些资源既可能来自本域,也可能来自外域;而任一资源变化,都可能大幅改变应用行为。应用必须对其所加载的资源做出承诺(commit)。也就是说,需要有机制让浏览器知道 “这是这个应用
一、背景
Apex 以 vscode 插件为主要载体,接入 SSO 认证、打通 CursorRules 知识库、Webview 远程 UI、实现无感安装 MCP、创建智能体、使用智能体等能力,帮助实现提示词撰写效率的提升,降低了使用过程的费力度。通过知识库、智能体等可实现在保障代码质量同时,进一步提升 AI 代码生成占比。
除了功能层面的能力,想必大家对 Apex 内部实现原理应该也很感兴趣,如何打通知识库、智能体使用时,MCP 为什么自动安装了,下面将从技术实现角度,剖析 Apex 如何将 “AI 能力” 工程化落地到 Cursor 开发流程中。了解 Apex 是如何激活装配、打通 SSO 认证,同步 Cursor Rules 知识库、通过远程 dist 包实现 webview UI 渲染,并提供智能体能力,实现无感更新,消息如何编排,如何识别大仓还是独立应用等。
二、架构设计总览

Apex 以插件为主控,Webview 承载 UI 与业务交互,服务层聚合认证、工程上下文、CursorRules 知识库、埋点等能力,MCP 以 “配置即工具” 的方式进行边界的扩展。实现三端(插件 - 前端 - 服务端)通过版本编排解耦 vscode 插件的迭代周期,在安全(鉴权)、可观测(日志与活跃情况)、工程落地(规则知识库与智能体模板)之间取得平衡。
三、功能设计 & 落地
3.1 激活与装配流程
为实现插件的稳定启动,在插件注册过程中,出现异常失败也不污染后续状态,通过事件注册按需加载,避免了冷启动插件带来的抖动问题。

要点流程介绍:
- 版本检查先行:防止低版本 Cursor 带来功能不兼容。
- 早期守卫 workspaceRoot,避免在无工作区时继续初始化产生隐式 NPE。
- 服务单例初始化顺序:ProjectService(工程上下文)→RuleSyncService(规则聚合 / 监听)→ StorageService(持久能力)→ AuthService.initialize ()(异步认证获取 token 与 userInfo)。
- 鉴权失败直接中断,避免后续埋点与 webview 的脏状态。
- 命令注册分散在此处,MessageHandler 负责 Webview 指令编排。

// ...
VersionChecker.checkVersion();
const root = workspaceRoot();
if (!root) return; // 早期守卫
ProjectService.getInstance(); // 工程上下文
const auth = AuthService.getInstance();
const logger = LoggerService.getInstance(context);
const user = await auth.initialize();
if (!user) return; // 认证失败即止损
logger.watch(); // 可观测
// 注册 Webview/命令/规则监听 ...
3.2 认证与安全
(AuthService+Storage)
通过接入 SSO 实现可以单点登录闭环 + 本地仅存最少信息,降低泄漏面、失败可针对进行快速反馈。后期记录用户维度智能体的使用、记录用户的关键行为埋点,从而进一步实现及时私聊沟通解决告警出现的问题,另外,针对用户的使用习惯和使用情况可进行针对性的分析和需求收集。



令牌获取流程(嵌入端口监听 + 浏览器回调):
async initialize(){
const saved = await storage.getAuthToken();
const token = saved?.trim()? saved : await login();
return await validateToken(token) ?? await login();
}
令牌回调服务器(端口探测 + CORS + 路由校验):
import http from 'http';
import * as vscode from 'vscode';
const LOWCODE_PLATFORM_API = 'https://xxx.yyy.zzz/ddd';
const PORT = 9527;
const findAvailablePort = (startPort: number, endPort: number = startPort + 10): Promise<number> => {
// 端口监听逻辑
};
export const requestToken = async () => {
// 验证token有效性
}
- 数据结构与算法设计:
- 端口探测算法:线性递增(最多 10 次),失败上抛;简单可靠,代价可接受。
- CORS 与 OPTIONS 预检处理;路由严格校验 zzz/ddd,仅取请求头 accesstoken。
- 超时控制(5 分钟)避免悬挂。
- 持久化:
// ... 省略若干
export class StorageService {
// ...
public async saveAuthToken(token: string): Promise<void> {
await this.secureStorage.saveSecret(
StorageService.CACHE_KEYS.AUTH_TOKEN,
token
);
}
public async getAuthToken(): Promise<string | undefined> {
return await this.secureStorage.getSecret(
StorageService.CACHE_KEYS.AUTH_TOKEN
);
}
public async deleteAuthToken(): Promise<void> {
await this.secureStorage.deleteSecret(
StorageService.CACHE_KEYS.AUTH_TOKEN
);
}
public async clearAll(): Promise<void> {
for (const key of Object.values(StorageService.CACHE_KEYS)) {
// 遍历清除key值
}
}
}
- 在 secrets 中存敏感数据,安全性较高;clearAll () 同时清理多个状态缓存值,防止残留。
3.3 规则知识库工程化
(RuleSyncService)
通过 Gtlab 维护远程知识库文档,实现知识库聚合,模板 / 规则 “一键对齐”,多包仓不会出现杂乱和上下文丢失等情况。大仓模式下实现批量并发拉取,非大仓模式下实现兜底向上拉取能力。
知识库规则拉取至各应用逻辑

模板拉取过程:GitLab 分批并发,chunk 化
export async function fetchTemplateFiles(
projectId: number,
templatePath: string,
branch: string
): Promise<Array<{ path: string; content: string }>> {
// git接口获取文件并进行分发同步
}
- 数据结构:数组分块 + Promise.all 并发,有效权衡吞吐与限流风险(每批 5 个)。
- 模板写入(按类型路由到 .cursor/rules/basic.mdc、notepads 或工程根):
export async function writeTemplatesToDisk(files, templatePath, targetRoot, type?) {
for (const file of files) {
// 处理模板写入
}
return true;
}
- .gitignore 同步策略(追加不重复的规则):
export const syncGitIgnoreToPath = async (targetPath: string, ignoreRules: string[]) => {
// 追加ignore逻辑
};
子应用规则同步至逻辑

监听与同步策略:
public startWatcher() {
const pattern = new vscode.RelativePattern(this.workspaceRoot, '*/**/.cursor/rules/*.mdc');
this.watcher = vscode.workspace.createFileSystemWatcher(pattern);
this.watcher.onDidCreate(uri => {
// 同步变更mdc
});
this.watcher.onDidChange(uri => {
// 同步变更mdc
});
this.watcher.onDidDelete(uri => {
// 移除指定mdc
});
}
规则改写:
export function writeRelativePathToContent(content: string, relativePath: string) {
// 相对路径注入 + 目标文件聚合为 .sync.mdc
}
- 算法说明:
- 提取 mdc 头中的 globs,对不含路径分隔符的 glob 自动加 /**/,再拼接相对路径前缀,确保规则定位到子包内。
- 默认兜底 **/*.*,覆盖子目录所有文件,提升易用性。
- 目标文件命名:
private getTargetPath(fsPath: string) {
// 针对同步过来的所有mdc文件进行重命名
}
- 将子仓的规则扁平化同步到根目录 .cursor/rules/*.sync.mdc,避免分散规则导致的遗漏。
3.4 远程 webview 与版本编排
(Webview+VersionChecker+Trace)
由于 Apex 插件的更新需要手动通过 dx vs 更新,修复问题或有新功能无法实时进行更新,新版本有问题无法回滚及时止损。
Apex 通过 DNF 接口获取当前远程版本 webVersion、coreVersion,对比当前 local 加载版本,实现无感更新或回退。
- 重新加载方式:直接拉取最新版本。
- Tab 重新点开,实时检测最新版本,点击更新按钮实现更新。

远程加载逻辑(支持本地调试、回退远端 CDN):
const v = await fetchWebVersion() || 'latest';
const local = useLocal() && await ping('http://localhost:9527/...');
const js = local ? mapToExternal(local) : cdn(`@apex-plugin/web@${v}`);
return htmlWith(js, csp());
- 关键要点:
- 从 DNF 后端接口获取 webVersion,优先 USE_LOCAL 且本地可访问则映射本地端口。
- 动态 CSP(当前较宽松,含 'unsafe-eval'),满足构建产物运行,未来会按资源域名白名单收紧。
- 版本编排:Web UI 可独立灰度;插件端仅负责加载版本号对应的资源。
Cursor 版本下限拦截:
export class VersionChecker {
private static readonly MIN_VERSION = "0.46.0";
public static async checkVersion() {
const isCursor = vscode.env.appName.toLowerCase().includes("cursor");
if (!isCursor) {
return;
}
// ... 读取本机 Cursor 安装目录,比较版本,小于阈值弹升级引导
}
// compareVersions(v1, v2) 三段位点比较
}
版本注入的位置(可用于埋点 / 展示):TraceService.setPluginVersion (v)+ getPluginVersion () 默认读取 core package.json。建议在宿主(packages/plugin)激活时注入真实发布版本,确保埋点准确。
3.5 项目服务(ProjectService):
Monorepo 识别、模板拉取与写入
通过识别应用是否是大仓应用,便于后期进行不同类型应用的业务逻辑处理,如知识库同步在大仓下和单仓下的不同分支逻辑是不同的。大仓下子仓的规则会被自动聚合到顶层.cursor/rules 下,实现规则命中率显著提升。
Monorepo 识别与项目列表收集方式:
public isMonorepo(): boolean {
try {
// 通过判断是否有`pnpm-workspace`判断是否是大仓
// 获取 package.json 中 workspaces 进行判断是否大仓
} catch (error) {
console.error('判断monorepo失败:', error);
return false;
}
}
3.6 埋点与活跃情况记录
(LoggerService+UsageRecorder)
通过组合心跳与焦点事件,确保用户离开窗口也能形成完整闭环记录,便于后期进行用户行为画像等分析。
事件监听与活跃态判定:
start() {
// 开启事件监听记录
this.eventList = [
// 多个事件记录绑定...
];
}
startInterval() {
if (!this.walkClocker) {
this.walkClocker = setInterval(() => { this.reportUsage(); }, this.walkInterval);
}
}
- walkInterval=30s 的心跳,配合窗口焦点事件强制上报一次,确保离开窗口时记录 “单次活跃时长”。
埋点上报示例:
const reportTimenote = async () => {
// 记录用户活跃情况、包含分支、版本、仓库等等信息
}
性能与可靠性:
- 事件监听广泛但回调轻量(更新时间 + 定时器驱动),无重 IO。
- 远端上报失败未中断主流程,可再考虑指数退避与采样策略。
更多推荐



所有评论(0)