Files
opsxshell/AGENTS.md
T
Logan.Li 514d62644e v2.0.0: 主题系统+SSH私钥解析修复+会话持久化+连接池优化+UI打磨
核心修复:
- ssh-service.ts: 新增 resolvePrivateKey(),将私钥文件路径(~/.ssh/id_rsa)解析为内容后传给 ssh2,
  解决 'Cannot parse privateKey: Unsupported key format' 错误
- useTerminalConnection.ts + sessionHelper.ts: 根据 authType 过滤字段,password/agent 认证不再传 privateKey
- sessionStore.ts: addSession() 现在先调用 sessionCreate() 持久化到 SQLite,解决恢复标签'无可用连接信息'问题
- database-service.ts: updateSession() 补充 private_key_path 字段持久化
- SessionDialog.tsx: 默认 username='root'、privateKey='~/.ssh/id_rsa'

UI/UX:
- 语义化主题系统(app-theme.ts),强制深色模式,全局无边框设计
- macOS 标题栏支持双击最大化(window:toggleMaximize IPC)
- macOS 菜单栏('OpsXShell' 应用菜单 + Settings Cmd+,)
- WelcomeView 4卡片布局,移除 Settings 按钮
- TabBar/TabItem 背景色统一(bg-dark-800/900),消除白色边框线

架构优化:
- SFTP 生命周期修复(useSFTP.ts useRef + autoConnectAttempted guard)
- 连接池竞态修复,支持快速多标签连接
- IPC 接口规范化,安全错误处理
- Vite 代码分割优化

质量: lint零错误 | tsc零错误 | build成功 | 49 tests pass
2026-04-22 11:15:41 +08:00

26 KiB
Raw Blame History

AGENTS.md - OpsXShell 项目开发指南

本文件为 AI 编程助手提供项目开发指导。


项目概述

OpsXShell 是跨平台 SSH/SFTP 终端工具 · 简洁高效 · 安全可靠 · 基于 Electron + React + TypeScript 构建。

核心功能

  • 🔐 SSH 终端 - 支持密码、密钥、SSH Agent 多种认证方式
  • 📁 SFTP 文件管理 - 双栏可视化文件管理,拖拽上传下载
  • 🔄 端口转发 - 本地/远程端口转发支持
  • 📑 多标签页 - 类浏览器标签管理,支持会话恢复
  • 🎨 现代 UI - 基于 Tailwind CSS 的深色主题界面
  • 🔒 安全可靠 - 敏感数据使用 AES-256-GCM 加密存储
  • 🌐 跨平台 - 支持 Windows、macOS、Linux
  • ⚡ Zmodem 支持 - 集成 rz/sz 文件传输协议

当前版本状态

  • 版本: v2.0.0
  • 状态: 已发布 (Release) - 安全增强版
  • 已完成: 核心 SSH/SFTP 功能、加密存储、多标签页系统、Zmodem 传输
  • v2.0 优化: safeStorage 主密钥加密、路径遍历防护、PBKDF2 600K 迭代、类型安全、工程化建设、性能优化
  • 技术债务: 部分组件仍在迁移到新的连接池架构

技术栈

层级 技术 版本
框架 Electron 28+
前端框架 React 18
语言 TypeScript 5
构建工具 Vite 5
状态管理 Zustand 4.4+
终端仿真 XTerm.js 5.5+
SSH/SFTP ssh2 1.15+
Zmodem zmodem-ts 1.0.8
数据库 better-sqlite3 9.4+
样式 Tailwind CSS 3.3+
图标 Lucide React 0.294+
测试 Vitest 4.1+

项目结构

opsxshell/
├── electron/                      # Electron 主进程
│   ├── main.ts                   # 主进程入口 - 窗口创建、应用生命周期、菜单
│   ├── preload.ts                # 上下文桥接,暴露 API 到渲染进程
│   ├── tsconfig.json             # 主进程 TS 配置 (CommonJS)
│   ├── ipc/                      # IPC 处理器(按功能模块组织)
│   │   ├── index.ts              # IPC 注册入口
│   │   ├── ssh.ipc.ts            # SSH 连接、Shell、端口转发
│   │   ├── sftp.ipc.ts           # SFTP 文件操作
│   │   ├── session.ipc.ts        # 会话 CRUD
│   │   ├── settings.ipc.ts       # 应用设置
│   │   ├── dialog.ipc.ts         # 文件对话框
│   │   ├── file.ipc.ts           # 文件系统操作
│   │   ├── zmodem.ipc.ts         # Zmodem 传输
│   │   └── app.ipc.ts            # 应用信息
│   └── services/                 # 主进程服务
│       ├── ssh-service.ts        # SSH 连接、Shell 会话、SFTP 操作
│       ├── database-service.ts   # SQLite 持久化存储
│       ├── crypto.ts             # AES-256-GCM 加密工具
│       ├── key-service.ts        # 主密钥管理
│       └── zmodem-service.ts     # Zmodem 文件传输服务
├── src/                          # 渲染进程 (React)
│   ├── components/               # UI 组件
│   │   ├── index.ts             # 🆕 统一导出
│   │   ├── App.tsx              # 根组件
│   │   ├── SessionDialog.tsx    # 会话编辑对话框
│   │   ├── QuickConnectDialog.tsx # 快速连接对话框
│   │   ├── WelcomeView.tsx      # 欢迎页面
│   │   │
│   │   ├── layout/              # 🆕 布局组件
│   │   │   ├── Sidebar.tsx      # 侧边栏
│   │   │   ├── TabBar.tsx       # 标签栏
│   │   │   ├── TabContent.tsx   # 标签内容
│   │   │   └── StatusBar.tsx    # 状态栏
│   │   │
│   │   ├── sftp/                # SFTP 模块
│   │   │   ├── index.ts
│   │   │   ├── SFTPView.tsx     # SFTP 主视图
│   │   │   ├── Breadcrumb.tsx
│   │   │   ├── ConnectionSelector.tsx
│   │   │   ├── ContextMenu.tsx
│   │   │   ├── FileRow.tsx
│   │   │   ├── SortHeader.tsx
│   │   │   ├── TransferItem.tsx
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   │
│   │   ├── terminal/            # 终端模块
│   │   │   ├── index.ts
│   │   │   ├── TerminalView.tsx # 终端视图
│   │   │   └── types.ts
│   │   │
│   │   ├── settings/            # 🆕 设置模块
│   │   │   └── SettingsView.tsx # 设置页面
│   │   │
│   │   └── common/              # 🆕 通用组件
│   │       ├── index.ts
│   │       ├── Button.tsx       # 按钮
│   │       ├── Modal.tsx        # 模态框
│   │       ├── Loading.tsx      # 加载状态
│   │       └── ErrorBoundary.tsx # 错误边界
│   ├── stores/                   # Zustand 状态管理
│   │   ├── index.ts             # 🆕 统一导出
│   │   ├── tabStore.ts          # 标签页状态管理
│   │   ├── settingsStore.ts     # 应用设置
│   │   ├── terminalSnapshotStore.ts # 终端快照
│   │   └── connection/          # 连接管理架构
│   │       ├── index.ts         # 统一导出
│   │       ├── types.ts         # 类型定义
│   │       ├── poolStore.ts     # 连接池核心
│   │       ├── lifecycleStore.ts # 连接生命周期
│   │       ├── storeHooks.ts    # Store Hooks
│   │       └── sessionHelper.ts # 会话配置获取
│   ├── themes/                   # 主题配置
│   │   └── terminal.ts          # 终端主题
│   │
│   ├── hooks/                    # 🆕 自定义 Hooks
│   │   ├── index.ts             # 统一导出
│   │   ├── useTerminal.ts       # 终端管理
│   │   ├── useSFTP.ts           # SFTP 操作
│   │   ├── useConnection.ts     # 连接管理
│   │   └── useSettings.ts       # 设置操作
│   │
│   ├── constants/                # 🆕 常量管理
│   │   ├── index.ts             # 统一导出
│   │   ├── terminal.ts          # 终端常量
│   │   ├── sftp.ts              # SFTP 常量
│   │   └── keybindings.ts       # 快捷键
│   │
│   ├── utils/                    # 工具函数
│   │   ├── index.ts             # 统一导出
│   │   ├── logger.ts            # 统一日志系统
│   │   ├── error-handler.ts     # 统一错误处理
│   │   ├── validators.ts        # 输入校验
│   │   └── formatters.ts        # 格式化工具
│   │
│   ├── electron.d.ts            # Electron API 类型声明
│   ├── main.tsx                 # React 渲染入口
│   └── index.css                # 全局样式(Tailwind + 自定义样式)
├── shared/                       # 共享类型定义(主进程/渲染进程共用)
│   └── types/
│       ├── session.types.ts     # 会话相关类型
│       ├── connection.types.ts  # 连接相关类型
│       ├── sftp.types.ts        # SFTP 类型
│       ├── ipc.types.ts         # IPC 类型
│       └── index.ts             # 统一导出
├── test/                         # 测试文件
│   ├── setup.ts                 # 测试环境配置(mock Electron API)
│   ├── stores/                  # Store 测试
│   │   ├── tabStore.test.ts
│   │   └── connectionPool.test.ts
│   └── components/sftp/         # 组件测试
│       └── utils.test.ts
├── dist/                         # 前端构建输出 (Vite)
├── dist-electron/                # 主进程构建输出
├── package.json                  # 项目配置 + electron-builder 配置
├── vite.config.ts               # Vite 配置
├── vitest.config.ts             # Vitest 测试配置
├── tailwind.config.js           # Tailwind CSS 配置
├── tsconfig.json                # 渲染进程 TS 配置
├── tsconfig.node.json           # Node 类型配置
├── index.html                   # 入口 HTML(含 CSP 配置)
└── .eslintrc.cjs                # ESLint 配置

架构详解

Electron 双进程模型

┌─────────────────────────────────────────────────────────────┐
│                     主进程 (Node.js)                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │  ssh-service │  │  database   │  │   key-service       │  │
│  │  (ssh2)     │  │  (SQLite)   │  │  (加密密钥管理)      │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
│  ┌─────────────┐  ┌─────────────┐                           │
│  │zmodem-service│  │   IPC       │                           │
│  │ (zmodem-ts) │  │  Handlers   │                           │
│  └─────────────┘  └──────┬──────┘                           │
│                          │                                  │
│              ipcMain.handle() / event.sender.send()        │
│                          │                                  │
│           ┌──────────────┴──────────────┐                  │
│           │      contextBridge           │                  │
│           │    (electron/preload.ts)     │                  │
│           └──────────────┬──────────────┘                  │
└──────────────────────────┼──────────────────────────────────┘
                           │ IPC (安全隔离)
┌──────────────────────────┼──────────────────────────────────┐
│                     渲染进程 (Chromium)                      │
│           ┌──────────────┴──────────────┐                   │
│           │      window.electronAPI      │                   │
│           └──────────────┬──────────────┘                   │
│                          │                                  │
│  ┌─────────────┐  ┌──────┴──────┐  ┌─────────────────────┐  │
│  │  React UI   │  │  Zustand    │  │   XTerm.js 终端     │  │
│  │  组件       │  │  Stores     │  │                     │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

IPC 通信接口

渲染进程通过 window.electronAPI 与主进程通信。完整类型定义在 src/electron.d.ts。

核心 IPC 通道

// SSH 操作
const { success, connectionId, error } = await window.electronAPI.sshConnect(config)
const { success, shellId } = await window.electronAPI.sshCreateShell(connectionId, { cols, rows })
await window.electronAPI.sshShellWrite(shellId, data)
await window.electronAPI.sshShellResize(shellId, cols, rows)
await window.electronAPI.sshDisconnect(connectionId)

// Shell 数据推送(主进程 → 渲染进程)
window.electronAPI.onShellData((event, connectionId, shellId, data: Buffer) => {
  terminal.write(data)
})

// SFTP 操作
const { sftpId } = await window.electronAPI.sftpConnect(connectionId)
const { files } = await window.electronAPI.sftpListDirectory(sftpId, path)
await window.electronAPI.sftpUpload(sftpId, localPath, remotePath)

// 会话管理
const { sessions } = await window.electronAPI.sessionGetAll()
const { session } = await window.electronAPI.sessionCreate(sessionData)
await window.electronAPI.sessionUpdate(id, updates)
await window.electronAPI.sessionDelete(id)

数据流

  1. SSH 连接流程:

    QuickConnectDialog/SessionDialog
           ↓
    window.electronAPI.sshConnect()
           ↓
    ipcMain.handle('ssh:connect')
           ↓
    ssh-service.connect()
           ↓
    ssh2.Client.connect()
    
  2. Shell 数据流:

    ssh2 ClientChannel (data事件)
           ↓
    event.sender.send('ssh:shellData')
           ↓
    ipcRenderer.on('ssh:shellData')
           ↓
    window.electronAPI.onShellData()
           ↓
    XTerm.write(data)
    
  3. 会话持久化:

    connectionStore.addSession()
           ↓
    window.electronAPI.sessionCreate()
           ↓
    database-service.createSession()
           ↓
    SQLite (密码加密存储)
    

构建和运行命令

环境要求

  • Node.js 18+
  • npm 9+ 或 yarn 1.22+

开发命令

# 安装依赖
npm install

# 启动开发服务器(Vite + Electron)
npm run dev
# 说明:会先启动 Vite dev server (端口 5173),然后启动 Electron

# 运行 ESLint 检查
npm run lint

# 运行测试
npm run test              # 运行所有测试
npm run test:watch        # 监视模式
npm run test:coverage     # 生成覆盖率报告

生产构建

# 构建生产版本(前端 + Electron 主进程)
npm run build

# 构建步骤分解:
# 1. tsc - 检查渲染进程 TypeScript
# 2. vite build - 构建前端到 dist/
# 3. npm run build:electron - 编译主进程到 dist-electron/

打包分发

# 打包当前平台
npm run dist

# 平台特定打包
npm run dist:win     # Windows (NSIS)
npm run dist:mac     # macOS (DMG, x64 + arm64)
npm run dist:linux   # Linux (AppImage)

# 输出目录: release/

代码规范

文件组织

  • 组件: src/components/*.tsx - PascalCase 命名
  • 子组件目录: src/components/{feature}/*.tsx - 功能相关子组件
  • Stores: src/stores/*Store.ts 或 src/stores/{feature}/store.ts - camelCase
  • 服务: electron/services/*-service.ts - kebab-case
  • IPC 处理器: electron/ipc/{feature}.ipc.ts - kebab-case
  • 类型: 共享类型定义在 shared/types/*.types.ts

命名约定

// 组件: PascalCase
export default function TerminalView() {}

// Hooks: camelCase + use 前缀
const useConnectionStore = create<ConnectionState>(...)

// 接口: PascalCase
interface SSHConfig { ... }
interface TabState { ... }

// 常量: UPPER_SNAKE_CASE (局部) 或 camelCase (模块级)
const ALGORITHM = 'aes-256-gcm'
const keyService = new KeyService()

类型安全

  • 所有 IPC 接口必须在 src/electron.d.ts 中声明
  • 共享类型定义在 shared/types/ 目录
  • 使用严格的 TypeScript 配置 (strict: true)
  • Zustand Store 必须定义 State 接口
// 示例:完整的类型定义
interface Tab {
  id: string
  title: string
  type: 'welcome' | 'terminal' | 'sftp' | 'settings'
  sessionId?: string
  connectionConfig?: SSHConnectionConfig
  isActive: boolean
}

interface TabState {
  tabs: Tab[]
  activeTabId: string | null
  addTab: (tab: Omit<Tab, 'id' | 'isActive'>) => void
  // ...
}

IPC 错误处理

所有 IPC 处理器必须返回统一的响应格式:

// 主进程
ipcMain.handle('xxx:action', async (event, params) => {
  try {
    const result = await doSomething(params)
    return { success: true, data: result }
  } catch (error: any) {
    return { success: false, error: error.message }
  }
})

// 渲染进程
const result = await window.electronAPI.xxxAction(params)
if (result.success) {
  // 处理成功
} else {
  // 处理错误: result.error
}

安全规范

✅ 必须遵守

  1. 始终启用 contextIsolation

    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,   // ✅ 必须
      nodeIntegration: false,   // ✅ 必须
    }
    
  2. 通过 preload 暴露 API

    // preload.ts - 唯一使用 node 能力的地方
    contextBridge.exposeInMainWorld('electronAPI', api)
    
  3. 敏感数据加密

    • 所有密码使用 simpleEncrypt/simpleDecrypt 加密存储
    • 主密钥存储在 app.getPath('userData')/encryption.key
    • 密钥文件权限设置为 0o600
  4. 资源清理

    app.on('before-quit', () => {
      sshService.disconnectAll()
      dbService?.close()
    })
    
  5. CSP 配置

    <meta http-equiv="Content-Security-Policy" 
          content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';">
    

❌ 禁止行为

  1. ❌ 在渲染进程直接使用 require('ssh2') 或其他 Node 模块
  2. ❌ 暴露 nodeIntegration: true 或 contextIsolation: false
  3. ❌ 明文存储密码或密钥
  4. ❌ 忽略 SSH 连接关闭(会导致资源泄漏)
  5. ❌ 事件监听不清理(组件卸载时调用 removeAllListeners)

日志与错误处理

统一日志系统

使用 src/utils/logger.ts 替代 console.log:

import { createLogger } from '../utils/logger'

const logger = createLogger('ModuleName')

// 使用
logger.debug('调试信息', { detail: 'xxx' })
logger.info('操作成功', { id: 'xxx' })
logger.warn('警告信息')
logger.error('操作失败', error)

日志级别:

  • DEBUG - 开发调试信息(生产环境不输出)
  • INFO - 关键操作记录
  • WARN - 警告信息
  • ERROR - 错误信息

统一错误处理

使用 src/utils/error-handler.ts:

import { handleError, createError, ErrorCode } from '../utils/error-handler'

// 1. 转换未知错误
try {
  await riskyOperation()
} catch (error) {
  const appError = handleError(error, 'context')
  // appError.message - 用户友好消息
  // appError.recoverable - 是否可恢复
  // appError.action - 建议操作
}

// 2. 创建特定错误
const error = createError(ErrorCode.CONNECTION_TIMEOUT, 'detail')

错误码分类:

  • 1xxx - 连接错误
  • 11xx - SSH 错误
  • 12xx - SFTP 错误
  • 13xx - 会话错误
  • 14xx - 验证错误
  • 9xxx - 系统错误

数据库架构

SQLite 数据库存储在 app.getPath('userData')/opsxshell.db:

表结构

-- 会话表
CREATE TABLE sessions (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  host TEXT NOT NULL,
  port INTEGER DEFAULT 22,
  username TEXT NOT NULL,
  auth_type TEXT CHECK(auth_type IN ('password', 'key', 'agent')),
  password TEXT,              -- 加密存储
  private_key_path TEXT,      -- 私钥路径(不加密)
  passphrase_encrypted TEXT,  -- 私钥密码(加密)
  group_name TEXT DEFAULT 'default',
  tags TEXT,                  -- JSON 数组
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 连接历史表
CREATE TABLE connection_history (
  id TEXT PRIMARY KEY,
  session_id TEXT REFERENCES sessions(id),
  connected_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  disconnected_at DATETIME,
  duration INTEGER,
  status TEXT
);

-- 设置表
CREATE TABLE settings (
  key TEXT PRIMARY KEY,
  value TEXT,                 -- JSON 格式
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 密钥表
CREATE TABLE keys (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  type TEXT CHECK(type IN ('rsa', 'ed25519', 'ecdsa')),
  private_key_encrypted TEXT NOT NULL,
  public_key TEXT,
  passphrase_encrypted TEXT,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 端口转发规则表
CREATE TABLE port_forwards (
  id TEXT PRIMARY KEY,
  session_id TEXT REFERENCES sessions(id),
  type TEXT CHECK(type IN ('local', 'remote', 'dynamic')),
  local_host TEXT,
  local_port INTEGER,
  remote_host TEXT,
  remote_port INTEGER,
  is_active BOOLEAN DEFAULT FALSE
);

测试策略

测试框架

  • 测试运行器: Vitest
  • 测试环境: jsdom
  • 断言库: @testing-library/jest-dom
  • React 测试: @testing-library/react

测试文件位置

test/
├── setup.ts              # 测试环境配置
├── stores/               # Store 测试
│   ├── tabStore.test.ts
│   └── connectionPool.test.ts
└── components/           # 组件测试
    └── sftp/
        └── utils.test.ts

编写测试

import { describe, it, expect, beforeEach } from 'vitest'
import { useTabStore } from '../../src/stores/tabStore'

describe('TabStore', () => {
  beforeEach(() => {
    // Reset store to initial state
    useTabStore.setState({
      tabs: [{ id: 'welcome', title: '欢迎', type: 'welcome', isActive: true }],
      activeTabId: 'welcome',
    })
  })

  it('应该添加新的终端标签', () => {
    const { addTab } = useTabStore.getState()
    addTab({ title: 'Terminal', type: 'terminal' })
    
    const { tabs } = useTabStore.getState()
    expect(tabs).toHaveLength(2)
  })
})

Mock Electron API

测试环境中已配置完整的 Electron API mock,详见 test/setup.ts。


开发指南

添加新的 IPC 接口

  1. 在 src/electron.d.ts 添加类型声明:

    interface ElectronAPI {
      newMethod: (param: string) => Promise<IPCResponse>
    }
    
  2. 在 electron/preload.ts 暴露 API:

    const api = {
      newMethod: (param: string) => ipcRenderer.invoke('xxx:newMethod', param),
    }
    
  3. 在 electron/ipc/ 添加处理器:

    // electron/ipc/feature.ipc.ts
    export const FEATURE_CHANNELS = {
      NEW_METHOD: 'xxx:newMethod',
    } as const
    
    export function registerFeatureIPCHandlers(options: Options): void {
      ipcMain.handle(FEATURE_CHANNELS.NEW_METHOD, async (event, param) => {
        try {
          const result = await doSomething(param)
          return { success: true, data: result }
        } catch (error: any) {
          return { success: false, error: error.message }
        }
      })
    }
    
  4. 在 electron/ipc/index.ts 注册处理器:

    import { registerFeatureIPCHandlers } from './feature.ipc'
    
    export function registerAllIPCHandlers(options: IPCOptions): void {
      registerFeatureIPCHandlers({ ... })
    }
    

添加新的 UI 组件

  1. 在 src/components/ 创建组件文件(PascalCase)
  2. 如果需要状态,在 src/stores/ 创建对应的 Store
  3. 在 TabContent.tsx 添加渲染逻辑(如果是新标签类型)

添加终端主题

  1. 在 src/themes/terminal.ts 添加主题配置:

    'my-theme': {
      name: 'My Theme',
      background: '#xxxxxx',
      foreground: '#xxxxxx',
      // ... 所有必需的颜色
    }
    
  2. 在 src/stores/settingsStore.ts 的 terminalTheme 类型中添加新主题


连接池架构(新)

项目正在迁移到新的连接池架构,位于 src/stores/connection/。

连接状态

type ConnectionState =
  | 'disconnected'   // 初始/已关闭
  | 'connecting'     // 正在连接
  | 'connected'      // 活跃连接
  | 'background'     // 后台保持
  | 'suspended'      // 挂起(长时间未操作)
  | 'error'          // 连接错误
  | 'reconnecting'   // 正在重连

使用新的连接管理

import { useConnectionStore, getSessionConfig } from '../stores/connection'

// 在组件中使用
const connectionStore = useConnectionStore()

// 创建连接
const result = await connectionStore.createConnection(tabId, config)

// 断开连接
await connectionStore.disconnectConnection(connectionId, 'reason')

旧版兼容

src/stores/connectionPool.ts 提供向后兼容的导出,旧代码仍可继续使用:

import { useConnectionPool } from '../stores/connectionPool'
// 实际指向新的实现

常见问题

问题 解决方案
开发时 Electron 无法加载页面 确保 Vite 开发服务器在端口 5173 运行
SSH 连接失败 检查主机、端口、认证方式配置;查看主进程控制台日志
XTerm.js 显示异常 确保容器有明确宽高,调用 fitAddon.fit()
数据库初始化失败 检查应用数据目录权限 (app.getPath('userData'))
加密解密失败 检查主密钥文件是否存在且权限正确
构建后应用白屏 检查 vite.config.ts 中 base: './' 配置
better-sqlite3 构建失败 运行 npm run rebuild 或 npx electron-rebuild

参考资源