feat: initial release v0.1.0
- KKTerm-style UI: Activity Rail, Connection Sidebar, Tabbed Workspace - Serial Port connection (System.IO.Ports) - SSH connection (SSH.NET, password/key/keyboard-interactive) - Local Shell via Windows ConPTY (PowerShell/Bash/Cmd) - VT100/ANSI terminal rendering (owner-drawn, double-buffered) - TTL script engine: send, sendln, wait, pause, timeout, if/while, messagebox - Group execution: waitall, sendlnall, sendlngroup (Barrier sync) - Run All / Run Group toolbar buttons - SFTP file browser sidebar (SSH.NET SftpClient) - PDU control via SNMP (pduconnect/pductrl commands) - Settings: Terminal prefs, Shell config, PDU monitoring - About page with changelog - AppSettings persisted to %LocalAppData%\ETTerms\ - Dark theme throughout (KKTerm purple accent)
This commit is contained in:
+535
@@ -0,0 +1,535 @@
|
||||
# ETTerms — Architecture
|
||||
|
||||
> A native Windows terminal workspace for **SSH** and **Serial Port** connections,
|
||||
> with a TeraTerm-compatible **TTL scripting engine**.
|
||||
> UI inspired by **KKTerm** (activity rail + tabbed session workspace + saved-connection sidebar);
|
||||
> script engine ported and extended from **MyTeraTerm** (`TTLInterpreter`).
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
ETTerms 是一個給工程師 / 韌體 / 硬體驗證人員用的**單一視窗終端機工作台**。它把日常會用到的兩種連線——**SSH**(連伺服器 / 嵌入式 Linux)與 **Serial Port**(連 UART / console / 開發板)——收進同一個 WinForms 視窗裡,用分頁(Tab)管理多條連線,左側有可存檔的連線清單(Saved Connections)。
|
||||
|
||||
核心差異化價值是**腳本自動化**:沿用並擴充 MyTeraTerm 既有的 **TTL(Tera Term Language)直譯器**,讓使用者既有的 `.ttl` 腳本(`send` / `sendln` / `wait` / `if` / `while` / `logopen` / `pductrl`…)可以直接重用,直接驅動原生 SSH / Serial channel,做到登入自動化、批次下命令、log 收集、PDU 電源控制等。
|
||||
|
||||
開發策略採「**GUI 先行**」:先把 KKTerm 風格的視窗外殼與分頁工作區做出來(可見、可切換、可關閉),再逐步把 Serial → SSH → VT100 渲染 → 腳本引擎一層層補上。
|
||||
|
||||
**目標使用者:** 單機桌面使用者(無多人 / 無登入系統)。所有連線資料存在本機 SQLite,密碼存在 Windows Credential Manager,不上雲、不回傳。
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology | 備註 |
|
||||
|-------|-----------|------|
|
||||
| UI Framework | **C# .NET 8 WinForms**(`net8.0-windows`) | 與 MyTeraTerm 同框架;本機已裝 .NET 8 Desktop Runtime + SDK 9/10(net8 targeting pack 自動還原)|
|
||||
| SSH | **SSH.NET**(`Renci.SshNet`) | 原生 SSH,支援 password / key / keyboard-interactive |
|
||||
| Serial | **System.IO.Ports** | 沿用 MyTeraTerm `ComPortBridge` 經驗 |
|
||||
| Terminal 渲染 | **自繪 VT100 / ANSI 控制項**(owner-drawn `Control`) | 解析 ANSI escape,雙緩衝繪字格 |
|
||||
| Script 引擎 | **TTLInterpreterLib**(從 MyTeraTerm 移植 + 擴充) | 改為驅動 `ISessionChannel` 而非 com0com bridge |
|
||||
| 連線儲存 | **SQLite**(`Microsoft.Data.Sqlite`) | 取代 KKTerm 的 SQLite store;存連線 metadata |
|
||||
| 祕密儲存 | **Windows Credential Manager**(DPAPI / CredMan) | 連線密碼、SSH key passphrase,不落地明碼 |
|
||||
| PDU 控制(選用) | **SnmpSharpNet** | 沿用 MyTeraTerm PDU 控制(`pductrl` / `pduconnect`) |
|
||||
| 日誌 | 自製 **AppLogger**(從 MyTeraTerm 移植) | 檔案 + Debug 雙輸出 |
|
||||
| 打包 | `dotnet publish` + (選用)Inno Setup / MSIX | 單機安裝,current-user |
|
||||
|
||||
> **與舊版 MyTeraTerm 的關鍵差異:** 舊版是把真正的 `ttermpro.exe`(TeraTerm)嵌進 Panel,靠 **com0com 虛擬 COM 對**攔截 serial 來跑腳本。ETTerms 改走**全原生**:SSH.NET 做 SSH、`System.IO.Ports` 做 serial、自繪 VT100 控制項做終端機畫面,**不再依賴外部 TeraTerm exe,也不再需要 com0com**。腳本引擎從「驅動 com0com bridge」改成「驅動原生 `ISessionChannel`」。
|
||||
|
||||
---
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ MainForm (WinForms Shell) │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌──────────────────┐ ┌────────────────────────────┐ │
|
||||
│ │ Activity │ │ Connection │ │ Tabbed Workspace │ │
|
||||
│ │ Rail │ │ Sidebar │ │ (一個 Tab = 一條 Session) │ │
|
||||
│ │ (左側圖示) │ │ (Saved │ │ │ │
|
||||
│ │ │ │ Connections) │ │ ┌──────────────────────┐ │ │
|
||||
│ │ ▣ Terminal │ │ ▸ SSH: srv-01 │ │ │ TerminalView │ │ │
|
||||
│ │ ▣ Scripts │ │ ▸ SSH: nas │ │ │ (自繪 VT100 控制項) │ │ │
|
||||
│ │ ▣ Settings │ │ ▸ COM3 @115200 │ │ │ │ │ │
|
||||
│ │ │ │ ▸ COM7 @9600 │ │ │ bytes ↑↓ │ │ │
|
||||
│ └────────────┘ └──────────────────┘ │ └──────────┬───────────┘ │ │
|
||||
│ └─────────────┼──────────────┘ │
|
||||
└─────────────────────────────────────────────────────┼─────────────────┘
|
||||
│
|
||||
┌────────────────────────────┼───────────────┐
|
||||
│ ISessionChannel (抽象) │ │
|
||||
│ ├─ SshChannel (SSH.NET) ─┘ │
|
||||
│ └─ SerialChannel (System.IO.Ports) │
|
||||
└──────────────┬──────────────────────────────┘
|
||||
│ Write(bytes) / DataReceived(bytes)
|
||||
┌──────────────┴──────────────────────────────┐
|
||||
│ ScriptEngine (TTLInterpreter, ported) │
|
||||
│ send / sendln / wait / if / while / │
|
||||
│ logopen / messagebox / pductrl ... │
|
||||
└──────────────┬──────────────────────────────┘
|
||||
│ (選用)
|
||||
┌──────┴───────┐
|
||||
│ SNMP PDU │ (SnmpSharpNet)
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
**資料流核心抽象:** 所有連線都實作 `ISessionChannel`(`Write(byte[])` + `event DataReceived`)。`TerminalView` 與 `ScriptEngine` 都只認得這個抽象,因此 SSH 與 Serial 對上層完全一致——這是讓「同一套腳本引擎驅動兩種連線」的關鍵設計。
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
ETTerms/
|
||||
│
|
||||
├── CLAUDE.md # 專案記憶 & 給 Claude 的指令
|
||||
├── README.md # 快速上手、打包說明
|
||||
├── ARCHITECTURE.md # 本文件
|
||||
├── .gitignore # 含 For_AI/ 與 build 產物規則
|
||||
├── ETTerms.sln
|
||||
│
|
||||
├── docs/
|
||||
│ ├── architecture.md # (本文件副本 / 連結)
|
||||
│ ├── ttl-script-reference.md # TTL 指令對照表(移植自 MyTeraTerm)
|
||||
│ ├── decisions/ # ADR:為何走原生而非嵌 TeraTerm
|
||||
│ └── runbooks/ # 操作手冊、常見問題(無實際密碼)
|
||||
│
|
||||
├── .claude/
|
||||
│ ├── settings.json
|
||||
│ └── skills/
|
||||
│
|
||||
├── tools/
|
||||
│ ├── scripts/ # 建置 / 打包腳本
|
||||
│ └── prompts/
|
||||
│
|
||||
├── For_AI/ # 🚫 gitignored — AI 協作素材
|
||||
│ ├── KKTerm-main/ # UI 參考專案(Tauri/React 工作台)
|
||||
│ └── MyTeraTerm/ # Script 參考專案(舊版 WinForms)
|
||||
│
|
||||
└── src/
|
||||
└── ETTerms/ # 主應用程式(WinForms)
|
||||
│
|
||||
├── Program.cs # 進入點
|
||||
├── ETTerms.csproj # net8.0-windows, UseWindowsForms
|
||||
│
|
||||
├── App/ # ── 視窗外殼 (UI Shell) ──
|
||||
│ ├── MainForm.cs # 主視窗:Rail + Sidebar + Workspace(含深色標題列 DWM)
|
||||
│ ├── MainForm.Designer.cs
|
||||
│ ├── Theme.cs # 全域深色配色 (KKTerm 風格)
|
||||
│ ├── ActivityRail.cs # 左側圖示列 (Terminal/Scripts/Settings)
|
||||
│ ├── ConnectionSidebar.cs# 仿 KKTerm 可編輯資料夾樹(搜尋/CRUD/拖曳分類)
|
||||
│ ├── Dialogs/ # ── 深色對話框 ──
|
||||
│ │ ├── DarkDialog.cs # 對話框基底(深色 + DWM 標題列)
|
||||
│ │ ├── TextPromptDialog.cs # 單行輸入(資料夾命名 / 改名)
|
||||
│ │ └── ConnectionEditDialog.cs # 連線新增/編輯(名稱+類型+詳細,Phase 2 擴充)
|
||||
│ └── Workspace/ # ── 可平鋪 (tiling) 的工作區 ──
|
||||
│ ├── WorkspaceView.cs # 工具列(grid 預設) + split 樹管理 + active pane 路由
|
||||
│ ├── PaneControl.cs # 單一格子:自繪迷你分頁列 + 內容 + +↔↕✕ 動作鈕
|
||||
│ └── ProportionalSplit.cs# 按比例縮放的 SplitContainer (巢狀組成任意佈局)
|
||||
│
|
||||
├── Terminal/ # ── 終端機渲染 ──
|
||||
│ ├── TerminalView.cs # 自繪 VT100 控制項 (owner-drawn)
|
||||
│ ├── AnsiParser.cs # ANSI/VT100 escape 解析狀態機
|
||||
│ ├── ScreenBuffer.cs # 字格緩衝 (rows×cols, 屬性/顏色)
|
||||
│ └── TerminalInput.cs # 鍵盤 → byte 序列 (含特殊鍵)
|
||||
│
|
||||
├── Sessions/ # ── 連線抽象 ──
|
||||
│ ├── ISessionChannel.cs # Write(byte[]) + event DataReceived
|
||||
│ ├── SshChannel.cs # SSH.NET 實作 (ShellStream)
|
||||
│ ├── SerialChannel.cs # System.IO.Ports 實作
|
||||
│ ├── ShellChannel.cs # Windows ConPTY 本機 Shell
|
||||
│ ├── SessionPage.cs # 一個分頁 = TerminalView + Channel + 狀態
|
||||
│ └── SessionManager.cs # 開 / 關 / 列舉所有 active session
|
||||
│
|
||||
├── Connections/ # ── 連線資料 ──
|
||||
│ ├── Connection.cs # 連線 metadata 模型
|
||||
│ ├── ConnectionStore.cs # SQLite CRUD
|
||||
│ └── CredentialVault.cs # Windows Credential Manager 封裝
|
||||
│
|
||||
├── Scripting/ # ── TTL 腳本引擎(移植 + 擴充)──
|
||||
│ ├── TTLInterpreter.cs # 主直譯器 (port 自 MyTeraTerm)
|
||||
│ ├── ScriptRunner.cs # 非同步執行 + 取消 + 進度事件
|
||||
│ ├── GroupSyncContext.cs # Group 同步 (Barrier):waitall / sendlnall / sendlngroup
|
||||
│ └── Pdu/
|
||||
│ └── PduController.cs# SnmpSharpNet PDU 控制 (pductrl / pduconnect)
|
||||
│
|
||||
└── Infrastructure/
|
||||
├── AppLogger.cs # 日誌 (port 自 MyTeraTerm)
|
||||
├── AppSettings.cs # 使用者偏好 (JSON, %LocalAppData%\ETTerms\settings.json)
|
||||
└── NativeTheme.cs # 深色標題列 (DWM)
|
||||
```
|
||||
|
||||
> **`For_AI/` 內含兩份參考專案**:`KKTerm-main`(UI 參考,Tauri+React 的 Windows 工作台)與 `MyTeraTerm`(Script 參考,舊版嵌 TeraTerm 的 WinForms)。整個 `For_AI/` 已 gitignore,僅供開發時對照,不進 repo。
|
||||
|
||||
> **無 `secret/` 資料夾:** ETTerms 是桌面單機 App,**沒有 DB 密碼 / 連線字串 / API key 之類的伺服端祕密,也無 compile-time secret**。連線密碼一律存 Windows Credential Manager(不落地明碼),因此不需要 `secret/` 集中管理機制。
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
連線 metadata 存在本機 SQLite(`%LocalAppData%\ETTerms\ettermsdb.sqlite`)。**密碼 / passphrase 不存在這裡**,只存一個指向 Windows Credential Manager 的 `CredentialKey`。
|
||||
|
||||
```csharp
|
||||
public enum ConnectionType
|
||||
{
|
||||
Ssh = 0,
|
||||
Serial = 1
|
||||
}
|
||||
|
||||
// 主連線模型
|
||||
public class Connection
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; } = ""; // 顯示名稱,例:srv-01 / COM3 board
|
||||
public ConnectionType Type { get; set; }
|
||||
public int SortOrder { get; set; } // sidebar 排序
|
||||
public string? GroupName { get; set; } // 選用:分組 (folder)
|
||||
public DateTime LastUsedUtc { get; set; }
|
||||
|
||||
// SSH 專用(Type == Ssh 時有效)
|
||||
public SshSettings? Ssh { get; set; }
|
||||
// Serial 專用(Type == Serial 時有效)
|
||||
public SerialSettings? Serial { get; set; }
|
||||
|
||||
// 指向 Windows Credential Manager 的 key,例:"ETTerms/{Id}"
|
||||
// 明碼密碼絕不存進 SQLite
|
||||
public string? CredentialKey { get; set; }
|
||||
}
|
||||
|
||||
public class SshSettings
|
||||
{
|
||||
public string Host { get; set; } = "";
|
||||
public int Port { get; set; } = 22;
|
||||
public string Username { get; set; } = "";
|
||||
public SshAuthMethod AuthMethod { get; set; } // Password / PrivateKey / KeyboardInteractive
|
||||
public string? PrivateKeyPath { get; set; } // key 檔路徑(passphrase 走 CredentialVault)
|
||||
}
|
||||
|
||||
public enum SshAuthMethod { Password = 0, PrivateKey = 1, KeyboardInteractive = 2 }
|
||||
|
||||
public class SerialSettings
|
||||
{
|
||||
public string PortName { get; set; } = "COM1"; // COM3, COM7...
|
||||
public int BaudRate { get; set; } = 115200;
|
||||
public int DataBits { get; set; } = 8;
|
||||
public Parity Parity { get; set; } = Parity.None; // System.IO.Ports.Parity
|
||||
public StopBits StopBits { get; set; } = StopBits.One;
|
||||
public Handshake Handshake { get; set; } = Handshake.None;
|
||||
public string NewLine { get; set; } = "\r\n"; // 送出換行序列
|
||||
}
|
||||
|
||||
// 終端機偏好(存 AppSettings,非每連線)
|
||||
public class TerminalProfile
|
||||
{
|
||||
public string FontFamily { get; set; } = "Cascadia Mono";
|
||||
public float FontSize { get; set; } = 11f;
|
||||
public int Cols { get; set; } = 80;
|
||||
public int Rows { get; set; } = 24;
|
||||
public string Theme { get; set; } = "dark"; // 配色名稱
|
||||
public int ScrollbackLines { get; set; } = 5000;
|
||||
}
|
||||
```
|
||||
|
||||
**SQLite Schema(單表即可起步):**
|
||||
|
||||
| 欄位 | 型別 | 說明 |
|
||||
|------|------|------|
|
||||
| `Id` | TEXT (GUID) | 主鍵 |
|
||||
| `Name` | TEXT | 顯示名稱 |
|
||||
| `Type` | INTEGER | 0=Ssh, 1=Serial |
|
||||
| `SortOrder` | INTEGER | sidebar 排序 |
|
||||
| `GroupName` | TEXT NULL | 分組 |
|
||||
| `LastUsedUtc` | TEXT | ISO8601 |
|
||||
| `SettingsJson` | TEXT | `SshSettings` / `SerialSettings` 序列化 |
|
||||
| `CredentialKey` | TEXT NULL | Credential Manager 索引鍵 |
|
||||
|
||||
---
|
||||
|
||||
## Authentication & Authorization
|
||||
|
||||
**不適用。** ETTerms 是單機桌面工具,沒有使用者帳號 / 登入 / 角色系統。
|
||||
|
||||
唯一相關的「認證」是**對外連線時的 SSH 認證**(password / private key / keyboard-interactive),其憑證透過 **Windows Credential Manager** 儲存與讀取,由 `CredentialVault.cs` 封裝。詳見 [Security Considerations](#security-considerations)。
|
||||
|
||||
---
|
||||
|
||||
## Key Pages / Features
|
||||
|
||||
ETTerms 是單視窗多分頁,沒有「路由」,以下以**功能面板**為單位描述。
|
||||
|
||||
### Activity Rail(左側圖示列)
|
||||
- 切換主檢視:**Terminal**(連線工作區)/ **Scripts**(腳本編輯與執行)/ **Settings**(偏好設定)
|
||||
- 仿 KKTerm 的 ActivityRail,hover 顯示 tooltip
|
||||
|
||||
### Connection Sidebar(Saved Connections,仿 KKTerm 資料夾樹)
|
||||
- **使用者可自建資料夾**,把連線分類組織成任意層的目錄樹(資料夾可巢狀)
|
||||
- 每個資料夾顯示**連線數量徽章**、可展開 / 收合(工具列有「全部展開 / 收合」)
|
||||
- **搜尋框**:依名稱 / 主機即時過濾,命中的分支自動展開
|
||||
- 圖示區分 SSH / Serial,顯示主機或 COM port + baud
|
||||
- 操作(右鍵選單 + 工具列按鈕):新增資料夾 / 新增連線 / 重新命名 / 刪除
|
||||
- **拖曳分類**:把連線或資料夾拖進別的資料夾(禁止拖進自己的子孫)
|
||||
- 雙擊連線 → 在 **active pane** 開啟;「快速連線」→ 不存檔的 ad-hoc 連線
|
||||
- Phase 1 資料存記憶體;**Phase 2 換成 `ConnectionStore`(SQLite)持久化**
|
||||
|
||||
### Workspace(可平鋪 tiling 的工作區)
|
||||
- 工作區可分割成多個 **pane(格子)**,自由排列(仿 KKTerm grid):
|
||||
- **Grid 預設**:工具列一鍵切 `1×1 / 1×2 / 2×1 / 2×2 / 2×3 / 3×3`
|
||||
- **手動分割**:每個 pane 右上角 `↔`(左右分割)/ `↕`(上下分割),拖格線(`ProportionalSplit`)按比例調整大小
|
||||
- 關閉某格時,兄弟節點自動補位(collapse split);保留至少一格
|
||||
- **每個 pane 內可再開多條連線分頁**(pane 自己的迷你 tab strip)
|
||||
- **active pane** 以強調色外框標示;側欄雙擊連線會開進 active pane
|
||||
- 一條連線分頁 = `SessionPage`(`TerminalView` + `ISessionChannel`,Phase 3+ 接上)
|
||||
|
||||
### Terminal View(自繪 VT100)
|
||||
- 接收 channel bytes → `AnsiParser` → `ScreenBuffer` → 繪製
|
||||
- 鍵盤輸入 → `TerminalInput` → byte 序列 → channel
|
||||
- 支援:scrollback、選取 / 複製、貼上、字型 / 配色(從 `TerminalProfile`)
|
||||
|
||||
### Script Editor / Runner(Scripts 檢視)
|
||||
- 載入 / 編輯 `.ttl` 腳本(語法沿用 MyTeraTerm)
|
||||
- 對「目前 active session」執行腳本;顯示執行進度(檔名 / 行號 / 當前指令)
|
||||
- 可取消執行(`Cancel()`);`logopen` 將輸出寫檔
|
||||
- 選用:PDU 控制指令(`pductrl` / `pduconnect`)走 SNMP
|
||||
|
||||
### Group 同步執行
|
||||
- 分頁可透過**右鍵 Tab** → 設為 Group 1 / 2 / 3(或取消),cell footer 顯示 `[Group1-A]` 標籤
|
||||
- Toolbar 的 `▶ Group1` / `▶ Group2` / `▶ Group3` 按鈕對整個 Group 同時跑同一份 `.ttl` 腳本
|
||||
- Group 模式支援同步指令:`waitall`(全員 wait 到關鍵字再繼續)、`sendlnall`(全員到齊後各自 sendln)、`sendlngroup`(指定 member 才送)
|
||||
- `▶ Run All` 和分頁 `▶ Script` 會拒絕含 Group 指令的腳本(彈 Warning)
|
||||
- 同步機制使用 `System.Threading.Barrier`(`GroupSyncContext`),確保成員在同步點等齊
|
||||
|
||||
### Settings
|
||||
- 終端機字型 / 字級 / 配色 / scrollback 行數
|
||||
- 預設換行序列、編碼
|
||||
- 視窗位置記憶
|
||||
|
||||
---
|
||||
|
||||
## Data Flow
|
||||
|
||||
**SSH session 端到端:**
|
||||
|
||||
```
|
||||
使用者雙擊 Sidebar 連線
|
||||
→ SessionManager.Open(connection)
|
||||
→ CredentialVault.Get(connection.CredentialKey) 讀密碼
|
||||
→ new SshChannel(SshSettings, credential)
|
||||
→ SSH.NET SshClient.Connect()
|
||||
→ ShellStream 建立
|
||||
→ new SessionPage(TerminalView, channel) 加入 WorkspaceTabs
|
||||
|
||||
執行期雙向資料流:
|
||||
鍵盤 → TerminalInput → bytes → SshChannel.Write() → ShellStream
|
||||
ShellStream → SshChannel.DataReceived(bytes) → TerminalView
|
||||
→ AnsiParser → ScreenBuffer → Invalidate() → 繪製
|
||||
```
|
||||
|
||||
**腳本驅動流(與互動式共用同一 channel):**
|
||||
|
||||
```
|
||||
ScriptRunner.RunAsync(scriptText, activeChannel)
|
||||
→ TTLInterpreter(channel)
|
||||
send/sendln → channel.Write(bytes)
|
||||
wait "xxx" → 監聽 channel.DataReceived,比對緩衝直到 match 或 timeout
|
||||
logopen/write→ StreamWriter 寫檔
|
||||
pductrl → PduController(SNMP set)→ PDU
|
||||
if/while → 依 result / 變數做流程控制
|
||||
→ StatusChanged 事件 → UI 顯示「檔名 第N行 指令」
|
||||
```
|
||||
|
||||
**Serial session:** 與 SSH 相同,只是 `ISessionChannel` 換成 `SerialChannel`(`System.IO.Ports.SerialPort` 的 `DataReceived` / `Write`)。上層 `TerminalView` 與 `TTLInterpreter` 完全不需改動——這正是 `ISessionChannel` 抽象的價值。
|
||||
|
||||
---
|
||||
|
||||
## Key Constraints & Business Rules
|
||||
|
||||
1. **單機、無雲:** 所有連線 metadata 存本機 SQLite,密碼存 Windows Credential Manager,不回傳任何遙測。
|
||||
2. **密碼絕不落地明碼:** SQLite 只存 `CredentialKey`,實際密碼 / passphrase 一律走 Credential Manager。
|
||||
3. **一條連線一個分頁:** 同一連線可開多個分頁(各自獨立 session),但每個分頁綁定一個 `ISessionChannel`。
|
||||
4. **腳本對 active session 執行:** TTL 腳本只作用在目前選定的分頁 channel,不會跨分頁亂送。例外:**Group 模式**下 `waitall` / `sendlnall` / `sendlngroup` 可跨同 Group 成員同步。
|
||||
5. **腳本可取消:** 長時間 `wait` / `while` 必須能被使用者中止(`isCancelled` 旗標 + `OperationCanceledException`)。
|
||||
6. **Serial port 互斥:** 一個 COM port 同時只能被一個 session 開啟;開啟前需檢查可用性。
|
||||
7. **VT 相容性以常見情境為準:** VT100 / 常見 ANSI 序列優先;冷門 escape 可後補,不阻塞 GUI 進度。
|
||||
8. **GUI 先行:** Phase 1–2 必須先讓視窗外殼 + 分頁 + 假連線可見可操作,再接真實 channel。
|
||||
9. **不依賴外部 exe:** 不嵌 TeraTerm、不需 com0com;全原生 .NET 元件。
|
||||
10. **UI 不可被 channel I/O 阻塞:** channel 讀寫在背景,UI 更新一律 `Invoke` 回 UI thread。
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- **密碼儲存:** 一律使用 **Windows Credential Manager**(透過 `CredentialVault.cs`)。SQLite 內只存索引 `CredentialKey`,無明碼。SSH private key passphrase 同理。
|
||||
- **SSH host key 驗證:** 首次連線顯示 host key 指紋供使用者確認(trust-on-first-use),記錄已信任的指紋,之後比對;指紋不符要警告。
|
||||
- **私鑰檔保護:** private key 路徑存設定,但不複製 key 內容進 repo / SQLite。
|
||||
- **輸入處理:** 終端機輸入直接透傳給遠端,不做 shell 注入解讀(本來就是終端機);但 UI 載入腳本檔時要防路徑穿越 / 過大檔。
|
||||
- **日誌不含密碼:** `AppLogger` 與 `logopen` 輸出不可寫入密碼 / passphrase;連線資訊只記主機 / port,不記 credential。
|
||||
- **無 `secret/` 資料夾:** ETTerms 無伺服端祕密 / DB 密碼 / compile-time secret,連線密碼一律走 Windows Credential Manager,因此不設 `secret/` 集中目錄,也不需要 publish 類腳本。若日後做 Release 程式碼簽章,簽章 `.pfx` 請放在 repo 外並以環境變數 / CI secret 傳入。
|
||||
- **設定檔權限:** SQLite 與設定檔放在 `%LocalAppData%\ETTerms\`,跟隨使用者帳號 ACL。
|
||||
|
||||
---
|
||||
|
||||
## Build & Setup Steps
|
||||
|
||||
```powershell
|
||||
# 0. 前置:.NET 8 SDK(或 SDK 9/10 + .NET 8 Desktop Runtime)。確認:
|
||||
dotnet --list-sdks
|
||||
dotnet --list-runtimes | findstr WindowsDesktop # 需有 8.0.x
|
||||
|
||||
# 1. 建立方案與專案(Phase 1)
|
||||
cd F:\10_AI\ETTerms
|
||||
dotnet new sln -n ETTerms
|
||||
dotnet new winforms -n ETTerms -o src\ETTerms -f net8.0
|
||||
dotnet sln add src\ETTerms\ETTerms.csproj
|
||||
|
||||
# 2. 安裝 NuGet 套件
|
||||
cd src\ETTerms
|
||||
dotnet add package SSH.NET # 原生 SSH
|
||||
dotnet add package System.IO.Ports # Serial
|
||||
dotnet add package Microsoft.Data.Sqlite # 連線 metadata
|
||||
dotnet add package SnmpSharpNet # 選用:PDU 控制
|
||||
# Windows Credential Manager:用 CredentialManagement 套件或 P/Invoke advapi32
|
||||
|
||||
# 3. 設定 csproj(手動確認)
|
||||
# <TargetFramework>net8.0-windows</TargetFramework>
|
||||
# <UseWindowsForms>true</UseWindowsForms>
|
||||
# <Nullable>enable</Nullable>
|
||||
|
||||
# 4. 建置與執行
|
||||
cd F:\10_AI\ETTerms
|
||||
dotnet build
|
||||
dotnet run --project src\ETTerms\ETTerms.csproj
|
||||
|
||||
# 5. 打包(後期 Phase)
|
||||
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained false
|
||||
# 後續可用 Inno Setup / MSIX 做安裝程式
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Phases
|
||||
|
||||
> 依使用者要求「**前面先把 GUI 做出來,後面再慢慢補功能**」排序:
|
||||
> Phase 1–2 先把 KKTerm 風格的視窗外殼、分頁、假連線清單做到「看得到、點得動」,
|
||||
> Phase 3 起才接真實 Serial / SSH channel,最後補腳本引擎與打包。
|
||||
|
||||
### Phase 1 — 專案骨架 + UI Shell(工作量:S)✅ 已完成
|
||||
**目標:** 專案能 `dotnet run` 啟動,KKTerm 風格的主視窗外殼可見:左側 Activity Rail、連線 Sidebar、右側分頁工作區(先放假分頁)。
|
||||
**包含:**
|
||||
- [x] `dotnet new winforms` 建立 `src/ETTerms`,設定 `net8.0-windows` / `UseWindowsForms` / `Nullable`
|
||||
- [x] `MainForm.cs` + `MainForm.Designer.cs`:三欄佈局(Rail / Sidebar / Workspace)
|
||||
- [x] `ActivityRail.cs`:Terminal / Scripts / Settings 三個圖示按鈕(hover tooltip)
|
||||
- [x] `ConnectionSidebar.cs`:用假資料填 TreeView(SSH / Serial 圖示)+資料夾樹 / 搜尋 / CRUD / 拖曳分類(提前實作)
|
||||
- [x] `Workspace/WorkspaceView.cs`(取代原訂 `WorkspaceTabs.cs`):可平鋪 tiling 工作區(grid 預設 + 手動分割 + pane 內迷你分頁),提前實作 Future Extension 的分割畫面
|
||||
- [x] `AppLogger.cs`:從 MyTeraTerm 移植日誌
|
||||
- [x] 深色主題:`Theme.cs` + `NativeTheme.ApplyDarkTitleBar`(DWM 深色標題列)+ 深色對話框基底
|
||||
**驗收條件:** ✅ `dotnet build` 通過(0 警告 0 錯誤);主視窗出現,可在 Rail 切檢視、Sidebar 看到假連線、可開關分頁與分割工作區。
|
||||
|
||||
### Phase 2 — 連線資料 + Sidebar 真資料(工作量:M)✅ 已完成
|
||||
**目標:** 連線清單改吃 SQLite 真資料,可新增 / 編輯 / 刪除連線,密碼存進 Credential Manager。
|
||||
**包含:**
|
||||
- [x] `Connection.cs`:`Connection` / `SshSettings` / `SerialSettings` / `ConnectionType` / `SshAuthMethod` 模型
|
||||
- [x] `ConnectionStore.cs`:SQLite CRUD(`%LocalAppData%\ETTerms\ettermsdb.sqlite` 自動建表,`SettingsJson` 存 Ssh/Serial)
|
||||
- [x] `CredentialVault.cs`:Windows Credential Manager 讀寫封裝(advapi32 P/Invoke,`CredWrite`/`CredRead`/`CredDelete`)
|
||||
- [x] 連線編輯對話框:依類型切換 SSH(host/port/user/auth/key/password)與 Serial(COM/baud/databits/parity/stopbits/handshake)兩種表單
|
||||
- [x] Sidebar 綁定 `ConnectionStore`,支援新增 / 編輯 / 刪除 / 拖曳分組 / `SortOrder` 排序持久化(資料夾路徑存於 `GroupName`)
|
||||
**驗收條件:** ✅ 新增一條 SSH 與一條 Serial 連線、重啟 App 後仍在;密碼存在 Credential Manager(SQLite 內看不到明碼)。
|
||||
|
||||
> **實作備註:** 資料夾以連線的 `GroupName`(`/`-join 路徑)持久化;含連線的資料夾會在重啟後由路徑重建,**空資料夾為 session-only**(不另設 folders 表)。密碼一律經 `CredentialVault` 寫入 Credential Manager,SQLite 僅存 `CredentialKey`(`ETTerms/{Id}`)。
|
||||
|
||||
### Phase 3 — Serial Port Session(工作量:M)✅ 已完成
|
||||
**目標:** 雙擊 Serial 連線可開分頁、收發資料;先用最陽春的文字框驗證資料流。
|
||||
**包含:**
|
||||
- [x] `Sessions/ISessionChannel.cs`:`Write(byte[])` + `event Action<byte[]> DataReceived` + `Open()` / `Close()`(: IDisposable)
|
||||
- [x] `Sessions/SerialChannel.cs`:`System.IO.Ports.SerialPort` 實作(開啟前向 SessionManager 占用 COM port、互斥、錯誤釋放)
|
||||
- [x] `Sessions/SessionPage.cs` + `Sessions/SessionManager.cs`:分頁內容(綁 channel)+ 全域 session 登錄 / COM 互斥 / 列舉
|
||||
- [x] 暫用 `RichTextBox`(ReadOnly,靠遠端 echo 顯示)打通「鍵盤→送出、收到→顯示」
|
||||
- [x] UI 跨執行緒安全:channel 在 handle 建立後才 Open,`DataReceived` 經 `BeginInvoke` 回 UI thread
|
||||
- [x] 連線資料流改打通:`ConnectionActivated` 改傳完整 `Connection`,`WorkspaceView.BuildPage` 依類型建 `SessionPage`(Serial) 或佔位(SSH);關分頁 / 關 pane 經 `SessionPage.Dispose` 釋放 COM port
|
||||
**驗收條件:** ✅ 編譯通過(0 警告 0 錯誤)。接一塊開發板 / com0com loopback,打字送得出去、回傳看得到,關分頁能正確釋放 COM port(需實機 `dotnet run` 驗證硬體收發)。
|
||||
|
||||
### Phase 4 — SSH Session(工作量:M)✅ 已完成
|
||||
**目標:** 雙擊 SSH 連線可登入遠端、互動式 shell 可用。
|
||||
**包含:**
|
||||
- [x] `Sessions/SshChannel.cs`:SSH.NET `SshClient` + `ShellStream`(背景執行緒 Connect 避免凍 UI),實作 `ISessionChannel`
|
||||
- [x] 三種認證:password / private key(passphrase 走 `CredentialVault`)/ keyboard-interactive
|
||||
- [x] Host key 指紋 TOFU:`Sessions/HostKeyStore.cs`(`known_hosts.txt`)首次記錄 SHA256、之後比對、不符中止並警告
|
||||
- [x] 連線錯誤(逾時 / 認證失敗 / 斷線)寫入終端機輸出 + `AppLogger`
|
||||
- [x] PTY resize:反射呼叫 ShellStream 底層 `_channel.SendWindowChangeRequest`(SSH.NET 未公開此 API)
|
||||
**驗收條件:** ✅ 編譯通過(0/0)。用 password 與 key 兩種方式各登入一台 SSH 主機,能跑 `ls` / `top` 等互動命令(需實機驗證)。
|
||||
|
||||
> **套件:** SSH.NET 2024.2.0(含 BouncyCastle.Cryptography 2.4.0)。
|
||||
|
||||
### Phase 5 — VT100 終端機控制項(工作量:L)✅ 已完成
|
||||
**目標:** 用自繪 VT100 控制項取代陽春文字框,正確顯示顏色 / 游標 / 清屏等 ANSI 行為。
|
||||
**包含:**
|
||||
- [x] `Terminal/ScreenBuffer.cs`:rows×cols 字格(`Cell{Ch,Fg,Bg,Attr}`,含 Bold/Underline/Inverse)+ scrollback + 滾動區(DECSTBM)+ alt screen
|
||||
- [x] `Terminal/AnsiParser.cs`:狀態機(Ground/Esc/CSI/OSC)—游標移動、SGR(16 色 / 256 色 / truecolor)、清屏 / 清行、scroll region、insert/delete line/char、alt screen(47/1047/1049)、DECTCEM(?25)/DECAWM(?7)/DECCKM(?1)+ `Palette`
|
||||
- [x] `Terminal/TerminalView.cs`:owner-drawn 雙緩衝、run 合併繪製、滾輪 scrollback、選取 / 複製(Ctrl+C、右鍵)/ 貼上(Ctrl+V、Shift+Insert、右鍵)
|
||||
- [x] `Terminal/TerminalInput.cs`:鍵盤 → byte 序列(方向鍵含 appCursor、Fn、Home/End/PgUp/PgDn、Enter/Tab/Backspace/Esc)
|
||||
- [x] 套用 `Terminal/TerminalProfile`(字型 / 字級 / scrollback 行數);resize 由 `OnSizeChanged` 算 cols/rows 並 `Resized` 事件通知 channel(PTY size)
|
||||
**驗收條件:** ✅ 編譯通過(0/0)。在 SSH 跑 `vim` / `htop` / `top` 顏色與版面正常、視窗 resize 通知遠端(需實機驗證)。
|
||||
|
||||
### Phase 6 — TTL 腳本引擎移植 + 執行 UI(工作量:L)
|
||||
**目標:** 移植 MyTeraTerm 的 `TTLInterpreter`,改為驅動 `ISessionChannel`,可對 active session 跑 `.ttl` 腳本。
|
||||
**包含:**
|
||||
- [x] `TTLInterpreter.cs`:從 MyTeraTerm 移植,建構子改吃 `ISessionChannel`(取代 `ComPortBridge`)
|
||||
- [x] 指令覆蓋:`send` / `sendln` / `pause` / `wait` / `timeout` / `flushrecv` / `logopen` / `logwrite` / `logclose` / `messagebox` / `if`-`elseif`-`else`-`endif` / `while`-`endwhile` / 變數指派
|
||||
- [x] `ScriptRunner.cs`:`async` 執行 + 取消(`Cancel()`)+ `StatusChanged`(檔名 / 行號 / 指令)事件
|
||||
- [x] Group 同步指令:`waitall` / `sendlnall` / `sendlngroup` + `GroupSyncContext`(Barrier 同步)
|
||||
- [x] `▶ Run All`(對所有 Serial 個別跑)+ `▶ Group1/2/3`(Group 同步跑)
|
||||
- [x] `▶ Script` / `▶ Run All` 拒絕含 Group 指令的腳本(彈 Warning)
|
||||
- [x] `docs/ttl-script-reference.md`:指令對照表(含 Group 指令)
|
||||
- [ ] SSH session 完整驗收(SSH 自動登入 + 下命令 + `logopen` 收 log)
|
||||
**驗收條件:** Serial 已驗收通過。SSH 尚待實機驗證:載入一支 `.ttl`(SSH 自動登入 + 下命令 + `logopen` 收 log),對 active session 跑完並產生 log 檔,中途可按停止中止。
|
||||
|
||||
### Phase 7 — Settings + About 頁面(工作量:S)✅ 已完成
|
||||
**目標:** Activity Rail 加入 Settings / About 獨立頁面,提供偏好設定入口與版本紀錄。
|
||||
**包含:**
|
||||
- [x] `ActivityRail` 新增 Settings / About 兩個 view(三圖示:▤ / ⚙ / ℹ)
|
||||
- [x] `MainForm` view 切換邏輯(Terminal / Settings / About 互斥顯示)
|
||||
- [x] `SettingsView.cs`:偏好設定頁面骨架(Phase 8 擴充內容)
|
||||
- [x] `AboutView.cs`:左側 App 資訊 + Developer + Tech Stack 卡片,右側 Changelog timeline
|
||||
- [x] Changelog 資料結構,新版本只需加一筆 `ChangelogEntry`
|
||||
**驗收條件:** ✅ 點 Activity Rail 可切換三個頁面;About 頁正確顯示版本、作者、changelog。
|
||||
|
||||
### Phase 8 — PDU 控制 + Settings 擴充 + Shell + SFTP(工作量:M)✅ 已完成
|
||||
**目標:** 補上 SNMP PDU 控制、偏好設定持久化、本機 Shell(ConPTY)、SFTP 瀏覽器。
|
||||
**包含:**
|
||||
- [x] `Scripting/Pdu/PduController.cs`:SnmpSharpNet 實作,接 `TTLInterpreter` 的 `pductrl` / `pduconnect`
|
||||
- [x] `Infrastructure/AppSettings.cs`:終端機偏好 / Shell 設定 / 視窗位置記憶(JSON 存 `%LocalAppData%\ETTerms\`)
|
||||
- [x] Settings 檢視 UI:Terminal 分頁(字型 / 配色 / scrollback / 預設換行 / Shell 設定 + 即時預覽)+ PDU 分頁(連線 / 狀態監控)
|
||||
- [x] `Sessions/ShellChannel.cs`:Windows ConPTY 本機 Shell(PowerShell / Bash / Cmd),完整 PTY 支援
|
||||
- [x] Sidebar SFTP 分頁:SSH SFTP 檔案瀏覽器(連線 / 導航 / 目錄列表)
|
||||
- [x] `docs/runbooks/troubleshooting.md`:常見問題(COM / SSH / TTL / PDU)
|
||||
- [ ] `dotnet publish` + Inno Setup / MSIX 安裝程式(待使用者指示)
|
||||
**驗收條件:** ✅ PDU 指令可用;Settings 重啟保留;Local Shell 行為正常(ConPTY);SFTP 可瀏覽遠端目錄。打包待後續。
|
||||
|
||||
---
|
||||
|
||||
## Future Extensions
|
||||
|
||||
這個版本**不做、但未來可能加**:
|
||||
|
||||
- ~~**SFTP 檔案瀏覽**~~(✅ 已於 Phase 8 實作:sidebar SFTP 分頁)
|
||||
- **Telnet** session 類型(補一個 `TelnetChannel : ISessionChannel`)
|
||||
- **RDP / VNC** 分頁(KKTerm 用 mstscax.dll;ETTerms 可後期評估)
|
||||
- **tmux 自動 attach**(SSH 斷線後自動回貼,仿 KKTerm)
|
||||
- ~~分割畫面 / 多 pane~~(✅ 已於 Phase 1 提前實作:`WorkspaceView` + `PaneControl` + `ProportionalSplit`)
|
||||
- **拖曳 pane 重新排列**(目前可分割 / 關閉 / 調大小,但還不能把 pane 拖到別處;未來可加 drag-drop 重排)
|
||||
- **佈局記憶**(記住上次的 grid / split 佈局,重啟還原)
|
||||
- **全新腳本語言或內嵌 Lua / C# scripting**(目前先沿用 TTL;未來可加第二引擎)
|
||||
- **連線分組 / 標籤 / 搜尋** 強化
|
||||
- **跨平台**(WinForms 綁 Windows;若要跨平台需評估 Avalonia / MAUI 重寫 UI 層,但 `Sessions` / `Scripting` 層因抽象良好可重用)
|
||||
- **macOS / Linux 終端機渲染** 改用 WebView2 + xterm.js(若要與 KKTerm 視覺對齊)
|
||||
|
||||
---
|
||||
|
||||
## Reference Projects(`For_AI/`)
|
||||
|
||||
| 專案 | 角色 | 取用重點 |
|
||||
|------|------|---------|
|
||||
| `KKTerm-main` | **UI 參考** | Activity Rail + 分頁工作區 + Saved Connections sidebar 的版面與互動;SQLite 存連線、Credential Manager 存密碼的 local-first 思路 |
|
||||
| `MyTeraTerm` | **Script 參考** | `lib/TTLInterpreter.cs` 的 TTL 指令實作(直接移植)、`AppLogger.cs` 日誌、PDU/SNMP 控制(`pductrl`/`pduconnect`);`ComPortBridge.cs` 的 serial 經驗 |
|
||||
|
||||
> 注意:KKTerm 是 Tauri/React,**不**直接複製程式碼,只取 UI/UX 與資料架構概念;ETTerms 是純 WinForms。MyTeraTerm 的 `TTLInterpreter` 則可大段移植,主要改建構子從 `ComPortBridge` 換成 `ISessionChannel`。
|
||||
Reference in New Issue
Block a user