AI + SSH = ?一个大胆的想法和 48 小时的 MVP

48 阅读10分钟

从周五晚上的突发奇想,到周日晚上的第一个可用版本,这是一场激动人心的开发冲刺。 在这里插入图片描述

项目信息

想体验 AI SSH Assistant 吗?

如果觉得有用,欢迎给个 ⭐️ Star 支持一下!

前言

在上一篇文章《从一次线上事故说起:我为什么要做 AI SSH 助手》中,我讲述了 AI SSH Assistant 这个想法的由来。

有了想法后,我决定:用一个周末的时间,做出一个最小可行产品(MVP)!

为什么这么急?因为:

  1. 💡 想法需要快速验证 - 到底可不可行,做出来才知道
  2. 避免过度设计 - MVP 强迫你只做核心功能
  3. 🔥 趁热打铁 - 灵感稍纵即逝,不做就凉了

这篇文章记录了那个激动人心的 48 小时。


周五晚上:技术选型

下班回家后,我打开电脑,开始思考技术选型。

前端框架:Vue 3 还是 React?

React 的优势:

  • ✅ 生态更大,组件库更多
  • ✅ 就业市场需求大
  • ✅ 我之前用过,比较熟悉

Vue 3 的优势:

  • ✅ 上手更快,API 更简洁
  • ✅ Composition API 很优雅
  • ✅ 中文文档友好
  • ✅ 打包体积更小

最终选择:Vue 3

理由:

  1. 个人项目,开发效率优先
  2. Vue 3 的 Composition API 写起来很爽
  3. 我想学习一下 Vue 3(顺便提升技能)

桌面框架:Electron 还是 Tauri?

Electron 的优势:

  • ✅ 生态成熟,VS Code 都在用
  • ✅ 文档完善,问题好解决
  • ✅ Node.js 集成,SSH 库丰富

Tauri 的优势:

  • ✅ 包体积小(10MB vs 100MB)
  • ✅ 内存占用低
  • ✅ 性能更好

最终选择:Electron

理由:

  1. 我不会 Rust(Tauri 的后端语言)
  2. SSH 功能需要 Node.js 的 ssh2 库
  3. 快速开发优先,性能后续优化

后端框架:Express 还是 Fastify?

Express 的优势:

  • ✅ 最流行,资料最多
  • ✅ 中间件生态完善

Fastify 的优势:

  • ✅ 性能更好(3 倍于 Express)
  • ✅ 内置 TypeScript 支持
  • ✅ 现代化的 API 设计

最终选择:Fastify

理由:

  1. 性能更好
  2. TypeScript 支持更好
  3. 想尝试新技术

AI 平台:OpenAI 还是 Claude?

OpenAI GPT-4 的优势:

  • ✅ 最成熟,能力最强
  • ✅ API 文档完善
  • ✅ 社区资源多

Anthropic Claude 的优势:

  • ✅ 上下文窗口更大(100K tokens)
  • ✅ 更安全,更可控
  • ✅ 价格更便宜

最终选择:都支持!

理由:

  1. 不同用户有不同偏好
  2. 多个 AI 平台可以互为备份
  3. 抽象一个 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)

踩坑记录:

  1. ❌ Vite 的 HMR 在 Electron 中不生效

    • ✅ 解决:配置 electron-vite
  2. ❌ 主进程和渲染进程的 TypeScript 配置冲突

    • ✅ 解决:分开配置 tsconfig.json
  3. ❌ 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 录制演示视频:

  1. 连接到测试服务器
  2. 用自然语言对话:"查看内存使用情况"
  3. AI 生成命令:free -h
  4. 执行命令,显示结果
  5. 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 

遇到的主要问题

  1. Electron 进程间通信

    • 问题:主进程和渲染进程通信很麻烦
    • 解决:使用 @electron-toolkit/preload 简化
  2. SSH 连接管理

    • 问题:ssh2 的 API 是回调式的,不好用
    • 解决:封装成 Promise
  3. AI 响应格式化

    • 问题:GPT-4 有时不按格式返回
    • 解决:使用 response_format: { type: 'json_object' }
  4. 终端样式

    • 问题:xterm.js 的默认样式不好看
    • 解决:自定义主题,参考 VS Code

收获与感悟

技术收获

  1. 学会了 Vue 3 的 Composition API

    • 比 Options API 更灵活
    • 代码复用更方便
  2. 掌握了 Electron 开发

    • 进程模型
    • IPC 通信
    • 打包发布
  3. 深入理解了 SSH 协议

    • 认证方式
    • 命令执行
    • 文件传输
  4. 学会了 AI Prompt 工程

    • 如何设计好的 System Prompt
    • 如何控制输出格式
    • 如何处理边界情况

产品感悟

  1. MVP 的重要性

    • 快速验证想法
    • 避免过度设计
    • 尽早获得反馈
  2. 用户体验很重要

    • 即使是 MVP,也要注重细节
    • 好的 UI 能提升使用体验
    • 错误提示要友好
  3. 技术选型要务实

    • 不要追求完美
    • 选择熟悉的技术
    • 开发效率优先

下一步计划

MVP 完成后,我计划:

  1. 完善功能

    • 文件管理(SFTP)
    • 多标签页
    • 命令历史
    • 快捷键
  2. 优化体验

    • 更好的 UI
    • 更快的响应
    • 更智能的 AI
  3. 开源发布

    • 完善 README
    • 录制演示视频
    • 发布到 GitHub
  4. 推广运营

    • 写技术博客
    • 分享到社区
    • 收集用户反馈

总结

从周五晚上的技术选型,到周日晚上的第一次成功运行,这 48 小时是一段激动人心的旅程。

虽然只是一个 MVP,功能还很简陋,但它证明了:AI + SSH 的结合是可行的,而且很有价值!

在下一篇文章中,我会详细讲述技术选型的思考过程,特别是为什么选择 Electron。


项目信息

想体验 AI SSH Assistant 吗?

如果觉得有用,欢迎给个 ⭐️ Star 支持一下!


关于作者

一名热爱开源的后端开发工程师,专注于 AI 与开发工具的结合。

欢迎关注我,后续会持续分享 AI SSH Assistant 的开发历程和技术细节!