Files
tergent-android/doc/SSH模块详细设计说明.md
2026-05-25 13:34:22 +00:00

17 KiB
Raw Permalink Blame History

Tergent SSH 终端模块 — 详细设计说明

版本: v1.3.2
最后更新: 2026-05-25
基于: hm/tergent-android (Gitea)


目录

  1. 项目概述
  2. 包结构
  3. SSH 核心模块
  4. UI 界面层
  5. 数据持久化
  6. 辅助功能模块
  7. 关键数据流
  8. 修改指南

1. 项目概述

Tergent 是基于 OpenClaw Android 源码的 SSH 终端 App,支持多会话、SFTP 文件传输、端口转发等企业级 SSH 功能。UI 采用 Jetpack Compose + Material3,深色终端风格。

包路径: ai.openclaw.app.ui.tergent — SSH 相关代码全部在此包下。
核心框架: Kotlin + Jetpack Compose + JSch (SSH 库) + kotlinx.serialization


2. 包结构

app/src/main/java/ai/openclaw/app/
├── ui/
│   ├── tergent/                          # SSH 终端模块(你主要关心的)
│   │   ├── SshTerminalManager.kt         # SSH 连接/通信核心引擎
│   │   ├── SshTerminalScreen.kt          # 终端界面(Compose UI
│   │   ├── SshHost.kt                    # 主机列表 + 多会话管理
│   │   ├── SshConfigStorage.kt           # 配置持久化(SharedPreferences
│   │   ├── SftpBrowserScreen.kt          # SFTP 文件浏览界面
│   │   ├── SftpFileInfo.kt               # SFTP 文件信息数据类
│   │   ├── PortForwardManager.kt         # 端口转发管理
│   │   ├── ConnectionTester.kt           # 连接测试工具
│   │   ├── QuickCommands.kt              # 快捷命令
│   │   ├── DevicesScreen.kt              # 设备管理页
│   │   ├── NotificationsScreen.kt        # 通知历史页
│   │   ├── DiagnosticsScreen.kt          # 诊断页
│   │   ├── TergentComponents.kt          # 公用 UI 组件
│   │   └── TergentTheme.kt              # 深色主题颜色定义
│   ├── ConnectTabScreen.kt              # 连接 Tab(入口页)
│   ├── PostOnboardingTabs.kt            # 主 Tab 导航
│   └── ...
├── node/                                 # 后台服务
│   ├── ConnectionManager.kt              # 网关连接管理
│   ├── InvokeDispatcher.kt              # 指令分发
│   └── ...
└── ...

3. SSH 核心模块

3.1 SshTerminalManager.kt — SSH 引擎

文件: app/src/main/java/ai/openclaw/app/ui/tergent/SshTerminalManager.kt
依赖: JSch (SSH 库) + Kotlin Coroutines

数据类型

类型 用途 关键字段
SshConfig SSH 连接配置数据类 host, port(默认22), username, password, displayName, group(分组), authMode("password"/"key"), savedKeyPath
SshConnectionState 连接状态的 StateFlow connected, host, displayName, lines(终端输出行列表), error, authMode
HostKeyInfo 主机密钥指纹 host, type(算法), fingerprint, key
KnownHostsStore known_hosts 存储 基于 SharedPreferences 的 `host

核心函数

函数 作用 参数 返回值 说明
connect(config) 密码方式 SSH 连接 SshConfig Boolean 发起连接,启动输出读取协程。超时15s
connectWithKey(config, keyPath, passphrase) 密钥方式 SSH 连接 SshConfig + 私钥路径 + 密码 Boolean 加载私钥 → 连接 → 启动读取
sendCommand(command) 发送指令 String Boolean 将指令 + \n 写入 OutputStream
disconnect() 断开连接 Unit 清理通道、会话、流
reconnect() 使用上次配置重连 Boolean 自动判断密码/密钥模式
isConnected() 连接状态查询 Boolean 返回 _connectionState.value.connected
openSftpChannel() 打开 SFTP 通道 Boolean 基于现有 SSH 会话开 SFTP
closeSftpChannel() 关闭 SFTP 通道 Unit 清理 SFTP
listFiles(path) SFTP 列出目录 String (默认".") List 排序:目录优先,按字母
downloadFile(remote, local) SFTP 下载 remote路径, local路径 Boolean
uploadFile(local, remote) SFTP 上传 local路径, remote路径 Boolean

内部函数

函数 作用 说明
readOutput() 循环读取终端输出(IO 线程) 使用 ByteArray(4096) 缓冲区,过滤 ANSI 转义序列,行缓存上限 5000 行
filterAnsi(text) 过滤 ANSI 转义序列 使用正则匹配 CSI 序列 (ESC[...) 和 OSC 序列 (ESC]...BEL)

关键状态流

_connectionState: MutableStateFlow<SshConnectionState>
  - connected: Boolean
  - host: String
  - lines: List<String>    ← 终端输出行,随 readOutput() 不断更新
  - error: String?

调用方通过 connectionState: StateFlow 收集实时输出。


3.2 SshTerminalScreen.kt — 终端 UI

文件: app/src/main/java/ai/openclaw/app/ui/tergent/SshTerminalScreen.kt
技术: Jetpack Compose + AndroidView(EditText)

Composable 函数

函数 作用 层级 说明
SshConnectDialog SSH 连接对话框 弹窗 支持密码/密钥两种认证方式,显示已保存连接列表
SshTerminalScreen 终端主界面 主要页面 终端输出 + 输入桥 + 工具栏

SshConnectDialog 的输入字段

  • host / port / username / password
  • 认证方式切换:密码 / 密钥
  • 私钥路径选择 + 密码
  • 已保存连接快速填充
  • 所有字段预填支持(initialConfig 参数)

SshTerminalScreen 的布局结构

Column
├── 工具栏 (Row)
│   ├── 断开按钮(exit)
│   ├── SFTP 切换按钮
│   ├── 快捷命令列表
│   ├── 端口转发设置
│   ├── 字体大小按钮
│   └── 新建连接按钮
├── 输出区 (LazyColumn)          ← 终端输出的主体
│   ├── items(state.lines)       ← 每行一个 Text
│   └── item(key="prompt")       ← 底部输入提示行(当前输入内容)
└── 输入桥 (Row + AndroidView)
    └── EditText                  ← 软键盘输入入口

输入逻辑

触发方式 位置 说明
EditText → setOnEditorActionListener AndroidView → EditText 输入法按 Send/Enter 时调用 sendCommand
EditText → TextWatcher.onTextChanged AndroidView → EditText 检测 \n 字符(部分输入法用此方式)
EditText → setOnKeyListener AndroidView → EditText 硬件键盘 Enter 键
终端区域点击 → requestEditTextFocus Column.onClick 点击输出区域重新唤起键盘

当前输入缓存: currentInput (remember { mutableStateOf("") })
输入框显示: 在 EditText 中显示输入内容,同时底部输出区最后一个 Text 也显示相同的 currentInput
连接后自动焦点: LaunchedEffect(state.connected) — 连接成功后延迟 200ms 自动弹出键盘


3.3 SshHost.kt — 主机管理 + 多会话

文件: app/src/main/java/ai/openclaw/app/ui/tergent/SshHost.kt
作用: 整个 SSH 功能的入口 Composable,管理所有活跃会话

Composable 函数

函数 作用 说明
SshHost 主入口 标题栏 + 搜索 + 会话 Tab + 内容区
ConnectionListView 连接列表(按组) 搜索过滤 → 分组 → LazyColumn
SavedConnectionItem 单个连接项 显示名称 + 地址 + 测试按钮 + 删除

数据结构

activeSessions: mutableStateListOf<SshTerminalManager>()  ← 所有活跃会话
selectedTabIndex: Int                                     ← 当前选中的 Tab
savedConfigs: List<SshConfig>                             ← 保存的连接列表

关键逻辑

  1. openSession(config): 如果密码为空且非密钥模式 → 弹出对话框让用户输入密码;否则立即创建 Tab → 异步连接
  2. closeSession(index): 断开连接 → 移除 Tab → 调整选中 Tab
  3. 密钥文件选择: 使用 ActivityResultContracts.OpenDocument() 系统文件选择器
  4. 空密码回填: 如果保存的连接密码为空,自动弹出预填充的 SshConnectDialog

4. UI 界面层

4.1 页面路由

PostOnboardingTabs
├── Tab: 连接 (ConnectTabScreen)
│   ├── SshHost  ← SSH 功能(你最关注的部分)
│   │   ├── SshConnectDialog (模态弹窗)
│   │   ├── SshTerminalScreen (Tab 内容)
│   │   └── SftpBrowserScreen (Tab 内容)
│   └── DevicesScreen
├── Tab: 对话 (ChatSheet)
├── Tab: 设置 (SettingsSheet)
└── Tab: 引导 (OnboardingFlow)

4.2 设计主题 (TergentTheme.kt)

深色终端风格,定义在 TergentTheme.kt 中。所有 UI 颜色在文件顶部作为私有常量定义:

  • bg = #0D1117 (背景)
  • surface = #161B22 (卡片)
  • surfaceAlt = #1C2333 (卡片强调)
  • textPrimary = #E6EDF3
  • accent = #3FB950 (绿色,连接成功)
  • accentBlue = #58A6FF (蓝色,交互)
  • danger = #F85149 (红色,断开)

5. 数据持久化

5.1 SshConfigStorage.kt — 配置存储

存储方式: Android SharedPreferences
Key 前缀: tergent_ssh_configs (文件)

函数 作用 数据格式
saveConnection(context, config) 保存连接配置 JSON 序列化后入 List, 存为 JSON 数组字符串
loadConnections(context) 加载所有连接 JSON 数组反序列化为 List
deleteConnection(context, host, port, username) 删除连接 从列表中移除并回写
saveCustomCommands(context, cmds) 保存快捷命令 JSON 序列化(List
loadCustomCommands(context) 加载快捷命令 JSON 反序列化
saveThemeMode(context, mode) 保存主题 字符串 "dark"/"light"
loadThemeMode(context) 加载主题 默认 "dark"
saveFontSize(context, size) 保存字体 "small"/"medium"/"large"
loadFontSize(context) 加载字体 默认 "medium"
fontSizeToSp(size) 转换字体大小 "small"→10sp, "medium"→12sp, "large"→14sp

5.2 SshHost.kt 中的密钥文件存储

私钥保存到 context.filesDir/ 下,文件名 ssh_key_{host}_{timestamp}.pem
密钥路径列表存为 tergent_ssh_configs Preference 中用 | 分隔的字符串。


6. 辅助功能模块

6.1 SftpBrowserScreen.kt — SFTP 文件浏览器

函数/类型 作用
SftpBrowserScreen(manager, ...) 主界面,文件列表
loadDir(path) 加载远程目录,更新文件列表
formatSize(bytes) 格式化文件大小 (B/KB/MB)
文件项点击 目录→进入,文件→下载提示
formatPermissions(attrs) 权限字符串 (rwxr-xr-x)
SftpFileInfo 数据类: name, isDir, size, lastModified, permissions

6.2 PortForwardManager.kt — 端口转发

函数/类型 作用
PortForward 数据类: localPort, remoteHost, remotePort, type(本地/远程)
addForward(session, forward) 添加端口转发规则
removeForward(session, forward) 移除端口转发
listForwards(session) 列出当前转发

6.3 ConnectionTester.kt — 连接测试

函数/类型 作用
testConnection(host, port, timeoutMs) 异步测试 TCP 可达性
TestResult 数据类: reachable, host, port, latencyMs, rawOutput

6.4 QuickCommands.kt — 快捷命令

函数/类型 作用
QuickCommand 数据类: name, command
loadCustomCommands(context) 加载自定义快捷命令
saveCustomCommand(context, cmd) 保存快捷命令
deleteCustomCommand(context, name) 删除快捷命令
QuickCommandBar(...) Compose UI 组件:底部快捷命令栏

6.5 TergentComponents.kt — 公用 UI 组件

组件 作用
TergentStatusPill 状态胶囊 (连接/断开/错误)
TergentPrimaryButton 主按钮
TergentOutlinedButton 描边按钮
TergentCard 卡片容器
TergentToggleChip 切换 Chip

7. 关键数据流

7.1 连接流程

用户点击连接
  ↓
openSession(config)
  ↓
(密码为空?)→ 是 → 弹出 SshConnectDialog 预填 → 用户输入密码 → openSession
  ↓ 否
创建 SshTerminalManager → 加入 activeSessions → 选中 Tab
  ↓
coroutineScope.launch { mgr.connect(config) }
  ↓
JSch获取Session → 打开ChannelShell → 设置PTY → 连接
  ↓
启动 readOutput() 协程(IO 线程)
  ↓
_connectionState.value 不断更新 lines + connected=true
  ↓
SshTerminalScreen 通过 collectAsState() 实时刷新 UI

7.2 指令发送流程

用户在 EditText 输入指令
  ↓
按 Enter
  ↓
[三个可能的触发路径之一]
  ├── setOnEditorActionListener (IME_ACTION_SEND/GO/DONE)
  ├── TextWatcher.onTextChanged (检测到 \n 字符)
  └── setOnKeyListener (硬件 KEYCODE_ENTER)
  ↓
manager.sendCommand(command)
  ↓
(command + "\n").toByteArray(UTF-8) → OutputStream.write() → flush()
  ↓
SSH 服务器执行指令,返回输出
  ↓
readOutput() 读到新数据 → 更新 _connectionState.lines
  ↓
UI 自动刷新最后几行

7.3 断开流程

用户点击断开 / Tab 关闭按钮
  ↓
closeSession(index) 或 mgr.disconnect()
  ↓
running.set(false) → readOutput() 退出循环
  ↓
closeSftpChannel() → channel.disconnect() → session.disconnect()
  ↓
_connectionState.value = SshConnectionState() (重置)

8. 修改指南

8.1 常见修改场景

修改终端输入行为

涉及文件: SshTerminalScreen.kt
位置: EditText 的 AndroidView → factory block(约第 520-580 行)
关键点:

  • setOnEditorActionListener — 处理输入法发送事件
  • TextWatcher — 检测 \n 字符
  • setOnKeyListener — 硬件键盘
  • 三个事件处理器需要触发 sendCommand,但要注意各处理器的 coroutineScope 生命周期

调整终端显示样式

涉及文件: SshTerminalScreen.kt
位置: 文件顶部颜色常量和 Text 组件的 style 参数
示例: 修改字体、字号、行距、配色

新增 SSH 认证方式

涉及文件:

  • SshTerminalManager.kt — 添加 connectWithCert(...) 等方法
  • SshTerminalScreen.kt — 在 SshConnectDialog 添加认证方式切换
  • SshConfig — 添加认证方式字段

修改连接保存逻辑

涉及文件: SshConfigStorage.kt
存储位置: context.getSharedPreferences("tergent_ssh_configs", 0)
数据格式: JSON 序列化的 List<SshConfig>

增加新的 SSH 功能(如 SCP 传输)

涉及文件:

  • SshTerminalManager.kt — 通过现有 Session 开 SCP 通道
  • 新文件或集成到 SftpBrowserScreen.kt

8.2 编译注意事项

  1. Gradle 版本: 推荐用 gradle assemblePlayDebug(系统 Gradle 9.4.1),wrapper 8.2 可能存在 bcprov 缓存 bug
  2. 依赖: JSch 通过 OpenClaw libs.versions.toml 管理
  3. 编码: 所有 .kt 文件必须 UTF-8(已有文件曾经有编码损坏问题)
  4. 两个 flavor: playthirdParty,当前用 playDebug
  5. thirdParty flavor 有重复文件 Bug: 如果改动了 main 中的文件可能需要在 thirdParty 中也同步改动,反之亦然

附录: 文件速查表

文件 行数(约) 核心职责 主要修改点
SshTerminalManager.kt 420 SSH 引擎, 连接/发送/断开/SFTP 认证逻辑, 输出解析, SFTP 功能
SshTerminalScreen.kt 800+ 终端 UI, 输入桥, 连接对话框 输入处理, 显示样式, 对话框字段
SshHost.kt 500+ 主机列表, 多会话管理, Tab 导航 连接列表 UI, 会话生命周期
SshConfigStorage.kt 100 配置读写 (SharedPreferences) 存储字段, 序列化格式
SftpBrowserScreen.kt 400 SFTP 文件浏览器 文件操作, 上传/下载 UI
PortForwardManager.kt 70 端口转发 添加/移除/列出规则
ConnectionTester.kt 60 TCP 连接测试 超时, 延迟检测
QuickCommands.kt 140 快捷命令 命令列表, 按钮栏
DevicesScreen.kt 420 设备管理 设备列表, 状态, 连接
DiagnosticsScreen.kt 300 诊断信息 传感器, 系统信息, 日志
NotificationsScreen.kt 200 通知历史 通知列表, 清除
TergentTheme.kt 50 颜色和字体常量 全局颜色配置
TergentComponents.kt 200 通用 UI 组件 按钮, 卡片, Chip