ProcessOutput() in the readLoop was processing every byte of SSH output,
looking for OSC 7 sequences. When these sequences split across read
boundaries (common with 32KB buffer), partial sequences leaked through
and corrupted xterm.js parser state — producing red/green color blocks
instead of text, and characters rendering at wrong widths.
Reverted readLoop to direct passthrough (v0.7.3 behavior). Also removed
shell integration injection (stty -echo + PROMPT_COMMAND) which caused
terminal mode disruption on macOS. Also removed 4px xterm padding that
could cause fitAddon cell width miscalculation.
CWD tracking will be re-implemented via a separate SSH exec channel
that polls pwd without touching the terminal data stream.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two missing halves of CWD tracking:
1. Frontend: useSftp now listens for ssh:cwd:{sessionId} Wails events
and calls navigateTo() when followTerminal is enabled (default: on).
2. Backend: re-added shell integration injection with stty -echo to
suppress visible command output. Leading space keeps it out of
shell history. Handles both bash (PROMPT_COMMAND) and zsh (precmd).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The "click to download" button in Settings > About only logged to
console. Now calls DownloadUpdate + ApplyUpdate via Wails bindings,
matching the working flow in StatusBar.vue. Added "downloading" state
with spinner.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
F-1 (Theme Application): Theme selection now applies to all active xterm.js
terminals at runtime via session store reactive propagation. TerminalView
watches sessionStore.activeTheme and calls terminal.options.theme = {...}.
F-5 (Tab Badges): isRootUser() now checks session.username, connection
options JSON, and connection tags — no longer hardcoded to false.
F-6 (Keyboard Shortcuts): Added Ctrl+W (close tab), Ctrl+Tab / Ctrl+Shift+Tab
(next/prev tab), Ctrl+1–9 (tab by index), Ctrl+B (toggle sidebar). Input
field guard prevents shortcuts from firing while typing.
F-7 (Status Bar Dimensions): StatusBar now reads live cols×rows from
sessionStore.activeDimensions. TerminalView hooks onResize to call
sessionStore.setTerminalDimensions(). Falls back to "120×40" until first resize.
F-10 (Multiple Sessions): Removed the "already connected" early-return guard.
Multiple SSH/RDP sessions to the same host are now allowed. Disambiguated tab
names auto-generated: "Asgard", "Asgard (2)", "Asgard (3)", etc.
F-11 (First-Run MobaConf): onMounted checks connectionStore.connections.length
after loadAll(). If empty, shows a dialog offering to import from MobaXTerm.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove naive-ui and @xterm/addon-webgl from frontend deps — neither is
imported anywhere in frontend/src; the entire UI is hand-rolled Tailwind
and the terminal uses only FitAddon/SearchAddon/WebLinksAddon (22 packages
removed, 0 vulnerabilities)
- Add 003_connection_history.sql migration — CREATE TABLE IF NOT EXISTS so
it is safe and idempotent on existing databases; tracks per-connection
session duration for frequency/history analytics
- Wire MobaConfImporter into the plugin registry in app.New() so the
registry is no longer empty at runtime; ImportMobaConf continues to call
the importer directly (GetImporter key is "MobaXTerm", not "mobaconf")
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Highlight text auto-copies to clipboard via onSelectionChange. Right-click
on terminal pastes from clipboard by writing to SSH stdin. Disables
xterm.js default right-click word-select behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes:
1. Streaming TextDecoder: a single TextDecoder instance with {stream: true}
persists across events. Split multi-byte UTF-8 sequences at Go read()
boundaries are now buffered and decoded correctly across chunks.
2. requestAnimationFrame batching: incoming SSH data is accumulated and
flushed to xterm.js once per frame instead of on every Wails event.
Eliminates the laggy typewriter effect when output arrives in small
chunks (which is normal for SSH PTY output).
3. PTY baud rate: bumped TTY_OP_ISPEED/OSPEED from 14400 (modem speed)
to 115200. Some remote PTYs throttle output to match the declared rate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
atob() returns a "binary string" where each byte is a Latin-1 char code.
Multi-byte UTF-8 sequences (box-drawing, em dashes, arrows) were split
into separate Latin-1 codepoints, producing mojibake. Now reconstructs
the raw byte array and decodes via TextDecoder('utf-8') before writing
to xterm.js.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gutted the copilot panel, OAuth integration, and AI service registration.
The copilot component files are left in place for future reference but
disconnected from the UI. The replacement will be an embedded local
terminal running `claude` with MCP tools for Wraith session access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DownloadUpdate expects *UpdateInfo (with downloadUrl), not a version string.
Frontend was passing latestVersion which caused a silent deserialization failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reads %USERPROFILE%\.claude\.credentials.json (or ~/.claude/.credentials.json),
extracts the access and refresh tokens, stores them encrypted in Wraith's vault.
Works when Wraith's own OAuth exchange fails. If Claude Code is authenticated
on the same machine, Wraith piggybacks on the existing token.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause of pubkey auth failure: SSH key credentials had no username,
so ConnectSSH defaulted to "root" and the server rejected the key.
The SSH key form in ConnectionEditDialog only had Name, PEM, Passphrase.
Added Username field between Name and PEM.
Delete your existing SSH key credentials and re-create them with the
correct username.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sidebar automatically switches from Connections to SFTP tab when an SSH
session becomes active. Added slog debug output to ConnectSSH showing
credentialID, vault state, and loaded credential details to diagnose
pubkey auth failures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Events.On callback gets a CustomEvent with .data property, not the base64
string directly. Added multi-format extraction with debug logging for
unexpected shapes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete button appears next to credential dropdown when a credential is
selected. Confirms before deleting, refreshes list after.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: frontend btoa() encoded the PEM before sending to Go []byte
parameter. Wails already base64-encodes []byte over JSON bridge, so the
vault stored base64(base64(pem)) — garbage. Fix: Go method now accepts
string, frontend sends raw PEM. Keys must be re-added after this update.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ConnectSSH returns NO_CREDENTIALS error when no credential is stored
- Frontend catches auth failures and prompts for username/password
- ConnectSSHWithPassword method for ad-hoc password auth
- Version loaded from Go backend (build-time -ldflags) in settings + unlock screen
- Connection errors shown as alert() instead of silent console.error
- Added UpdateService.CurrentVersion() and WraithApp.GetVersion()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete connection/group now calls real Go backend (was local array splice)
- Duplicate connection calls ConnectionService.CreateConnection
- Rename group calls new ConnectionService.RenameGroup method
- Added Group+ and Host+ buttons to sidebar header
- Vault change password wired to real unlock/create flow
- Export/import vault shows helpful path info instead of stub alert
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical path wired end-to-end:
- ConnectSSH on WraithApp resolves credentials from vault, builds auth methods
- SSH output handler emits Wails events (base64) to frontend
- useTerminal.ts forwards keystrokes to SSHService.Write, resize to SSHService.Resize
- useTerminal.ts listens for ssh:data:{sessionId} events and writes to xterm.js
- session.store.ts connect() calls real Go ConnectSSH, not mock
- useSftp.ts calls real SFTPService.List instead of hardcoded mock data
- SFTP client auto-registered on SSH connection via pkg/sftp
- DisconnectSession cleans up both SSH and SFTP
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace all TODO stubs in frontend stores with real Wails Call.ByName
bindings. The app store now calls WraithApp.IsFirstRun/CreateVault/Unlock
so vault state persists across launches. The connection store loads from
ConnectionService.ListConnections/ListGroups instead of hardcoded mock
data. The import dialog calls a new WraithApp.ImportMobaConf method that
parses the file, creates groups and connections in SQLite, and stores
host keys. ConnectionEditDialog also uses real Go CRUD calls. MainLayout
loads connections on mount after vault unlock.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace mock responses in the XO copilot panel with real Wails binding
calls to the Go AIService backend:
- StartLogin now opens the browser via pkg/browser.OpenURL
- SendMessage returns ChatResponse (text + tool call results) instead of
bare error, fixing the tool-call accumulation bug in messageLoop
- Add GetModel/SetModel methods for frontend model switching
- Frontend useCopilot composable calls Go via Call.ByName from
@wailsio/runtime, with conversation auto-creation, auth checks, and
error display in the chat panel
- Store defaults to isAuthenticated=false; panel checks auth on mount
- CopilotSettings syncs model changes and logout to the backend
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add internal/updater package with UpdateService that queries the Gitea
generic-package API for newer releases, downloads the installer with
SHA256 verification, and launches it to apply the update. Includes
semver comparison (CompareVersions) and comprehensive test coverage
with httptest-based mock servers.
Wire UpdateService into WraithApp (app.go accepts version param) and
register as a Wails service in main.go. Frontend StatusBar shows a
blue pill notification when an update is available; SettingsModal About
section displays the current version and a "Check for Updates" button
with idle/checking/found/up-to-date/error states.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Settings button (gear icon) now opens SettingsModal with General, Terminal, Vault, About sections
- File menu added to toolbar with New Connection, Import MobaXTerm, Settings, Exit
- Command Palette "Settings" action now opens the settings modal
- Command Palette "New SSH/RDP Connection" actions now open ConnectionEditDialog
- ConnectionEditDialog mounted in MainLayout for File menu / palette access
- All Wails binding calls left as TODO with mock behavior
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>