Skip to content

Commit 06fef19

Browse files
feat: add 5 major SSH/Terminal features for v0.14.0
## 🚀 New Features ### 1. SSH Agent Forwarding - Auto-detect SSH agent socket (SSH_AUTH_SOCK, macOS/Linux common paths) - Forward local SSH agent to remote hosts via jump hosts - Per-connection setting in Advanced tab ### 2. Zmodem File Transfer (lrzsz) - Send/receive files over terminal using rz/sz commands - Automatic detection of Zmodem sequences in terminal stream - Full IPC handlers for file transfer workflow - React hook `useZmodem()` for UI integration - Requires lrzsz installed on remote host ### 3. True Color (24-bit) & Sixel Graphics - Enable 24-bit color support in terminal - Sixel graphics support for displaying images in terminal - Per-connection setting: xterm-256color-tc-sixel terminal type ### 4. SSH ProxyJump Chain Support - Added `proxyJumpChain` field for future multi-hop SSH chains - Infrastructure ready for complex SSH routing scenarios ### 5. Keep-Alive & Anti-Idle - **SSH Keep-Alive**: Automatic ping packets at configurable intervals - **Anti-Idle**: Send null bytes or custom strings to prevent timeout - Configurable interval, count max, and idle string per connection ## 🔧 Technical Changes ### Database Schema (SQLite) - Added 11 new columns to `connections` table: - `agent_forwarding`, `keepalive_interval`, `keepalive_count_max` - `ready_timeout`, `proxy_jump_chain` - `anti_idle`, `anti_idle_interval`, `anti_idle_string` - `zmodem_enabled`, `true_color_enabled`, `sixel_enabled` ### IPC/API - New `window.api.zmodem` namespace with 6 methods - Updated `SshConnectPayload` with new SSH options - Enhanced `ssh:connect` handler with agent forwarding & keepalive ### UI - ConnectionDialog Advanced tab expanded with: - SSH Agent Forwarding toggle - Keep-alive settings (interval, count max, timeout) - Anti-idle settings (enable, interval, string) - Zmodem file transfer toggle - Terminal display options (True Color, Sixel) ## ✅ Compatibility - TypeScript: 0 errors - Build: Successful for all platforms - Migration: Automatic ALTER TABLE for existing databases ## 📦 Files Changed - 13 files modified, 2 new files - ~1,200 lines added
1 parent 1d6b5e5 commit 06fef19

14 files changed

Lines changed: 1225 additions & 20 deletions

File tree

.cursorignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "netcopilot",
3-
"version": "0.13.1",
3+
"version": "0.14.0",
44
"description": "NetCopilot – AI-powered SSH/Telnet terminal for network engineers",
55
"main": "./out/main/index.js",
66
"author": {

src/main/db.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@ function initSchema(db: Database.Database): void {
7474
auto_reconnect INTEGER NOT NULL DEFAULT 1,
7575
reconnect_delay INTEGER NOT NULL DEFAULT 10,
7676
proxy_config TEXT,
77+
agent_forwarding INTEGER DEFAULT 0,
78+
keepalive_interval INTEGER DEFAULT 30,
79+
keepalive_count_max INTEGER DEFAULT 3,
80+
ready_timeout INTEGER DEFAULT 30,
81+
proxy_jump_chain TEXT,
82+
anti_idle INTEGER DEFAULT 0,
83+
anti_idle_interval INTEGER DEFAULT 60,
84+
anti_idle_string TEXT,
85+
zmodem_enabled INTEGER DEFAULT 0,
86+
true_color_enabled INTEGER DEFAULT 0,
87+
sixel_enabled INTEGER DEFAULT 0,
7788
created_at INTEGER NOT NULL,
7889
updated_at INTEGER NOT NULL,
7990
last_connected_at INTEGER
@@ -129,6 +140,31 @@ function initSchema(db: Database.Database): void {
129140
db.exec("ALTER TABLE connections ADD COLUMN proxy_config TEXT")
130141
}
131142

143+
// Migration for new SSH Advanced Settings columns
144+
const newColumns = [
145+
{ name: 'agent_forwarding', type: 'INTEGER DEFAULT 0' },
146+
{ name: 'keepalive_interval', type: 'INTEGER DEFAULT 30' },
147+
{ name: 'keepalive_count_max', type: 'INTEGER DEFAULT 3' },
148+
{ name: 'ready_timeout', type: 'INTEGER DEFAULT 30' },
149+
{ name: 'proxy_jump_chain', type: 'TEXT' },
150+
{ name: 'anti_idle', type: 'INTEGER DEFAULT 0' },
151+
{ name: 'anti_idle_interval', type: 'INTEGER DEFAULT 60' },
152+
{ name: 'anti_idle_string', type: 'TEXT' },
153+
{ name: 'zmodem_enabled', type: 'INTEGER DEFAULT 0' },
154+
{ name: 'true_color_enabled', type: 'INTEGER DEFAULT 0' },
155+
{ name: 'sixel_enabled', type: 'INTEGER DEFAULT 0' },
156+
]
157+
158+
for (const col of newColumns) {
159+
if (!colNames.has(col.name)) {
160+
try {
161+
db.exec(`ALTER TABLE connections ADD COLUMN ${col.name} ${col.type}`)
162+
} catch (e) {
163+
console.warn(`[db] Failed to add column ${col.name}:`, e)
164+
}
165+
}
166+
}
167+
132168
const blRow = db
133169
.prepare("SELECT value FROM settings WHERE key = 'ai.blacklist'")
134170
.get() as { value: string } | undefined
@@ -282,6 +318,21 @@ export function rowToConnection(row: Row): Connection {
282318
autoReconnect: Boolean(row.auto_reconnect),
283319
reconnectDelay: row.reconnect_delay as number,
284320
proxyConfig: row.proxy_config ? safeJsonParse(row.proxy_config as string, undefined) : undefined,
321+
// SSH Advanced Settings
322+
agentForwarding: Boolean(row.agent_forwarding),
323+
keepAliveInterval: (row.keepalive_interval as number) ?? 30,
324+
keepAliveCountMax: (row.keepalive_count_max as number) ?? 3,
325+
readyTimeout: (row.ready_timeout as number) ?? 30,
326+
proxyJumpChain: row.proxy_jump_chain ? safeJsonParse(row.proxy_jump_chain as string, undefined) : undefined,
327+
// Anti-idle
328+
antiIdle: Boolean(row.anti_idle),
329+
antiIdleInterval: (row.anti_idle_interval as number) ?? 60,
330+
antiIdleString: (row.anti_idle_string as string) || undefined,
331+
// Zmodem
332+
zmodemEnabled: Boolean(row.zmodem_enabled),
333+
// Terminal display
334+
trueColorEnabled: Boolean(row.true_color_enabled),
335+
sixelEnabled: Boolean(row.sixel_enabled),
285336
createdAt: row.created_at as number,
286337
updatedAt: row.updated_at as number,
287338
lastConnectedAt: (row.last_connected_at as number) || undefined,
@@ -310,6 +361,21 @@ export function connToRow(c: Connection): Row {
310361
auto_reconnect: c.autoReconnect ? 1 : 0,
311362
reconnect_delay: c.reconnectDelay ?? 10,
312363
proxy_config: c.proxyConfig ? JSON.stringify(c.proxyConfig) : null,
364+
// SSH Advanced Settings
365+
agent_forwarding: c.agentForwarding ? 1 : 0,
366+
keepalive_interval: c.keepAliveInterval ?? 30,
367+
keepalive_count_max: c.keepAliveCountMax ?? 3,
368+
ready_timeout: c.readyTimeout ?? 30,
369+
proxy_jump_chain: c.proxyJumpChain ? JSON.stringify(c.proxyJumpChain) : null,
370+
// Anti-idle
371+
anti_idle: c.antiIdle ? 1 : 0,
372+
anti_idle_interval: c.antiIdleInterval ?? 60,
373+
anti_idle_string: c.antiIdleString ?? null,
374+
// Zmodem
375+
zmodem_enabled: c.zmodemEnabled ? 1 : 0,
376+
// Terminal display
377+
true_color_enabled: c.trueColorEnabled ? 1 : 0,
378+
sixel_enabled: c.sixelEnabled ? 1 : 0,
313379
created_at: c.createdAt,
314380
updated_at: c.updatedAt,
315381
last_connected_at: c.lastConnectedAt ?? null,

0 commit comments

Comments
 (0)