从周五晚上的突发奇想,到周日晚上的第一个可用版本,这是一场激动人心的开发冲刺。
项目信息
想体验 AI SSH Assistant 吗?
- 🌟 GitHub 仓库:github.com/aifuqiang02…
- 📦 下载体验:github.com/aifuqiang02…
- 💬 QQ 交流群:307460844 - 点击加入
如果觉得有用,欢迎给个 ⭐️ Star 支持一下!
前言
在上一篇文章《从一次线上事故说起:我为什么要做 AI SSH 助手》中,我讲述了 AI SSH Assistant 这个想法的由来。
有了想法后,我决定:用一个周末的时间,做出一个最小可行产品(MVP)!
为什么这么急?因为:
- 💡 想法需要快速验证 - 到底可不可行,做出来才知道
- ⏰ 避免过度设计 - MVP 强迫你只做核心功能
- 🔥 趁热打铁 - 灵感稍纵即逝,不做就凉了
这篇文章记录了那个激动人心的 48 小时。
周五晚上:技术选型
下班回家后,我打开电脑,开始思考技术选型。
前端框架:Vue 3 还是 React?
React 的优势:
- ✅ 生态更大,组件库更多
- ✅ 就业市场需求大
- ✅ 我之前用过,比较熟悉
Vue 3 的优势:
- ✅ 上手更快,API 更简洁
- ✅ Composition API 很优雅
- ✅ 中文文档友好
- ✅ 打包体积更小
最终选择:Vue 3
理由:
- 个人项目,开发效率优先
- Vue 3 的 Composition API 写起来很爽
- 我想学习一下 Vue 3(顺便提升技能)
桌面框架:Electron 还是 Tauri?
Electron 的优势:
- ✅ 生态成熟,VS Code 都在用
- ✅ 文档完善,问题好解决
- ✅ Node.js 集成,SSH 库丰富
Tauri 的优势:
- ✅ 包体积小(10MB vs 100MB)
- ✅ 内存占用低
- ✅ 性能更好
最终选择:Electron
理由:
- 我不会 Rust(Tauri 的后端语言)
- SSH 功能需要 Node.js 的 ssh2 库
- 快速开发优先,性能后续优化
后端框架:Express 还是 Fastify?
Express 的优势:
- ✅ 最流行,资料最多
- ✅ 中间件生态完善
Fastify 的优势:
- ✅ 性能更好(3 倍于 Express)
- ✅ 内置 TypeScript 支持
- ✅ 现代化的 API 设计
最终选择:Fastify
理由:
- 性能更好
- TypeScript 支持更好
- 想尝试新技术
AI 平台:OpenAI 还是 Claude?
OpenAI GPT-4 的优势:
- ✅ 最成熟,能力最强
- ✅ API 文档完善
- ✅ 社区资源多
Anthropic Claude 的优势:
- ✅ 上下文窗口更大(100K tokens)
- ✅ 更安全,更可控
- ✅ 价格更便宜
最终选择:都支持!
理由:
- 不同用户有不同偏好
- 多个 AI 平台可以互为备份
- 抽象一个 AI Service 层,支持多平台
技术栈确定
经过一番思考,最终确定的技术栈:
前端:Vue 3 + TypeScript + Tailwind CSS
桌面:Electron
后端:Node.js + Fastify
数据库:SQLite (本地) / PostgreSQL (云端)
AI:OpenAI + Anthropic + Google Gemini
SSH:ssh2
终端:xterm.js
周六上午:搭建项目框架
1. 初始化项目(9:00 - 10:00)
# 创建项目目录
mkdir ai-ssh-assistant
cd ai-ssh-assistant
# 初始化 pnpm workspace(我喜欢用 pnpm)
pnpm init
# 创建 monorepo 结构
mkdir -p apps/desktop
mkdir -p apps/web
mkdir -p packages/server
mkdir -p packages/shared
mkdir -p packages/database
为什么用 monorepo?
- 前端、后端、共享代码在一个仓库
- 方便代码复用
- 统一依赖管理
2. 配置 Electron + Vue(10:00 - 11:30)
这是最麻烦的部分,需要配置:
- Vite(构建工具)
- Electron Builder(打包工具)
- TypeScript
- Vue Router
- 进程间通信(IPC)
踩坑记录:
-
❌ Vite 的 HMR 在 Electron 中不生效
- ✅ 解决:配置
electron-vite
- ✅ 解决:配置
-
❌ 主进程和渲染进程的 TypeScript 配置冲突
- ✅ 解决:分开配置
tsconfig.json
- ✅ 解决:分开配置
-
❌ IPC 类型定义很麻烦
- ✅ 解决:使用
@electron-toolkit/preload
- ✅ 解决:使用
3. 搭建基础 UI(11:30 - 12:30)
参考 VS Code 的布局,设计了一个三栏结构:
┌─────────────────────────────────────┐
│ Activity Bar │ Sidebar │ Main │
│ (活动栏) │ (侧边栏) │ (主区) │
│ │ │ │
│ 🏠 欢迎 │ SSH 列表 │ 对话 │
│ 🔌 SSH │ │ 界面 │
│ 💬 AI 对话 │ │ │
│ 📁 文件 │ │ │
│ ⚙️ 设置 │ │ │
└─────────────────────────────────────┘
使用 Tailwind CSS 快速实现:
- 深色主题(程序员最爱)
- 响应式布局
- VS Code 风格的颜色
周六下午:实现核心功能
1. SSH 连接管理(13:30 - 15:00)
首先实现最基础的功能:SSH 连接。
数据模型设计:
interface SSHConnection {
id: string
name: string // 连接名称
host: string // 服务器地址
port: number // 端口(默认 22)
username: string // 用户名
authType: 'password' | 'privateKey' // 认证方式
password?: string // 密码(加密存储)
privateKey?: string // 私钥(加密存储)
}
安全性考虑:
- 密码和私钥必须加密存储
- 使用 Node.js 的
crypto模块 - 密钥存储在系统 Keychain(macOS)或 Credential Manager(Windows)
实现代码(简化版):
import { Client } from 'ssh2'
class SSHService {
private client: Client | null = null
async connect(config: SSHConnection) {
this.client = new Client()
return new Promise((resolve, reject) => {
this.client!
.on('ready', () => {
console.log('SSH 连接成功!')
resolve(true)
})
.on('error', (err) => {
console.error('SSH 连接失败:', err)
reject(err)
})
.connect({
host: config.host,
port: config.port,
username: config.username,
password: config.password,
privateKey: config.privateKey,
})
})
}
async executeCommand(command: string): Promise<string> {
return new Promise((resolve, reject) => {
this.client!.exec(command, (err, stream) => {
if (err) return reject(err)
let output = ''
stream
.on('data', (data: Buffer) => {
output += data.toString()
})
.on('close', () => {
resolve(output)
})
})
})
}
}
2. AI 对话功能(15:00 - 17:00)
接下来是最核心的功能:AI 对话。
流程设计:
用户输入 → AI 理解意图 → 生成命令 → 用户确认 → 执行命令 → 返回结果 → AI 分析结果
Prompt 设计(这是关键):
const systemPrompt = `你是一个 Linux 服务器管理助手。
用户会用自然语言描述他们想做什么,你需要:
1. 理解用户的意图
2. 生成对应的 Linux 命令
3. 解释命令的作用
4. 分析命令执行结果
输出格式:
{
"command": "要执行的命令",
"explanation": "命令的解释",
"risk": "low|medium|high"
}
示例:
用户:"查看内存使用情况"
你的回复:
{
"command": "free -h",
"explanation": "查看系统内存使用情况,-h 参数以人类可读的格式显示",
"risk": "low"
}
`
集成 OpenAI API:
import OpenAI from 'openai'
class AIService {
private openai: OpenAI
constructor(apiKey: string) {
this.openai = new OpenAI({ apiKey })
}
async chat(message: string): Promise<AIResponse> {
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: message }
],
response_format: { type: 'json_object' }
})
const content = response.choices[0].message.content
return JSON.parse(content)
}
}
3. 整合流程(17:00 - 18:00)
把 SSH 和 AI 整合起来:
async function handleUserMessage(message: string) {
// 1. AI 生成命令
const aiResponse = await aiService.chat(message)
// 2. 显示命令,等待用户确认
showCommandPreview(aiResponse.command, aiResponse.explanation)
// 3. 用户确认后执行
if (await waitForUserConfirmation()) {
const output = await sshService.executeCommand(aiResponse.command)
// 4. 显示结果
showCommandOutput(output)
// 5. AI 分析结果
const analysis = await aiService.analyzeOutput(output)
showAnalysis(analysis)
}
}
周日上午:完善功能
1. 添加终端模拟器(9:00 - 11:00)
虽然有 AI 对话,但有时候还是需要直接输入命令。
集成 xterm.js:
pnpm add xterm xterm-addon-fit
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
const terminal = new Terminal({
theme: {
background: '#1e1e1e',
foreground: '#d4d4d4',
}
})
const fitAddon = new FitAddon()
terminal.loadAddon(fitAddon)
terminal.open(document.getElementById('terminal')!)
fitAddon.fit()
// 连接到 SSH
terminal.onData((data) => {
sshService.write(data)
})
sshService.onData((data) => {
terminal.write(data)
})
2. 错误处理(11:00 - 12:00)
添加各种错误处理:
- SSH 连接失败
- AI API 调用失败
- 命令执行超时
- 网络异常
try {
await sshService.connect(config)
} catch (error) {
if (error.code === 'ECONNREFUSED') {
showError('无法连接到服务器,请检查 IP 和端口')
} else if (error.code === 'EAUTH') {
showError('认证失败,请检查用户名和密码')
} else {
showError(`连接失败:${error.message}`)
}
}
周日下午:测试和打磨
1. 功能测试(13:00 - 15:00)
测试各种场景:
✅ 基础功能
- SSH 连接(密码和私钥)
- AI 对话
- 命令执行
- 结果展示
✅ 边界情况
- 网络断开
- 命令执行超时
- AI API 限流
- 特殊字符处理
✅ 用户体验
- 加载状态
- 错误提示
- 操作反馈
2. UI 优化(15:00 - 16:30)
细节打磨:
- 添加加载动画
- 优化颜色和间距
- 添加图标
- 响应式布局
3. 录制演示视频(16:30 - 17:30)
使用 OBS Studio 录制演示视频:
- 连接到测试服务器
- 用自然语言对话:"查看内存使用情况"
- AI 生成命令:
free -h - 执行命令,显示结果
- AI 分析结果:"内存使用率 45%,正常"
录制完成后,用 FFmpeg 转成 GIF:
ffmpeg -i demo.mp4 -vf "fps=10,scale=800:-1" demo.gif
激动人心的第一次运行
周日晚上 18:00,所有功能开发完成。
我深吸一口气,启动应用,开始第一次完整的测试:
场景:查看服务器内存
我输入:
这台服务器内存使用情况怎么样?
AI 回复:
我来帮你查看内存使用情况。
命令:free -h
解释:查看系统内存使用情况,-h 参数以人类可读的格式显示
风险等级:低
是否执行?
我点击"执行"
命令输出:
total used free shared buff/cache available
Mem: 7.8G 3.2G 1.1G 234M 3.5G 4.1G
Swap: 2.0G 0B 2.0G
AI 分析:
内存使用情况分析:
- 总内存:7.8GB
- 已使用:3.2GB (41%)
- 可用内存:4.1GB (53%)
结论:内存使用率正常,还有充足的可用内存。
成功了!!!
那一刻,我激动得差点跳起来。虽然只是一个简单的功能,但它真的能工作!
MVP 完成!
周日晚上 19:00,48 小时的开发冲刺结束。
实现的功能
✅ 核心功能
- AI 对话界面
- SSH 连接管理
- 命令生成和执行
- 结果展示和分析
- 终端模拟器
✅ 基础功能
- 连接列表
- 暗色主题
- 错误处理
- 加载状态
代码统计
文件数:42
代码行数:约 3000 行
提交次数:37 次
遇到的主要问题
-
Electron 进程间通信
- 问题:主进程和渲染进程通信很麻烦
- 解决:使用
@electron-toolkit/preload简化
-
SSH 连接管理
- 问题:ssh2 的 API 是回调式的,不好用
- 解决:封装成 Promise
-
AI 响应格式化
- 问题:GPT-4 有时不按格式返回
- 解决:使用
response_format: { type: 'json_object' }
-
终端样式
- 问题:xterm.js 的默认样式不好看
- 解决:自定义主题,参考 VS Code
收获与感悟
技术收获
-
学会了 Vue 3 的 Composition API
- 比 Options API 更灵活
- 代码复用更方便
-
掌握了 Electron 开发
- 进程模型
- IPC 通信
- 打包发布
-
深入理解了 SSH 协议
- 认证方式
- 命令执行
- 文件传输
-
学会了 AI Prompt 工程
- 如何设计好的 System Prompt
- 如何控制输出格式
- 如何处理边界情况
产品感悟
-
MVP 的重要性
- 快速验证想法
- 避免过度设计
- 尽早获得反馈
-
用户体验很重要
- 即使是 MVP,也要注重细节
- 好的 UI 能提升使用体验
- 错误提示要友好
-
技术选型要务实
- 不要追求完美
- 选择熟悉的技术
- 开发效率优先
下一步计划
MVP 完成后,我计划:
-
完善功能
- 文件管理(SFTP)
- 多标签页
- 命令历史
- 快捷键
-
优化体验
- 更好的 UI
- 更快的响应
- 更智能的 AI
-
开源发布
- 完善 README
- 录制演示视频
- 发布到 GitHub
-
推广运营
- 写技术博客
- 分享到社区
- 收集用户反馈
总结
从周五晚上的技术选型,到周日晚上的第一次成功运行,这 48 小时是一段激动人心的旅程。
虽然只是一个 MVP,功能还很简陋,但它证明了:AI + SSH 的结合是可行的,而且很有价值!
在下一篇文章中,我会详细讲述技术选型的思考过程,特别是为什么选择 Electron。
项目信息
想体验 AI SSH Assistant 吗?
- 🌟 GitHub 仓库:github.com/aifuqiang02…
- 📦 下载体验:github.com/aifuqiang02…
- 💬 QQ 交流群:307460844 - 点击加入
如果觉得有用,欢迎给个 ⭐️ Star 支持一下!
关于作者
一名热爱开源的后端开发工程师,专注于 AI 与开发工具的结合。
欢迎关注我,后续会持续分享 AI SSH Assistant 的开发历程和技术细节!