Commit Graph

29 Commits

Author SHA1 Message Date
Vantz Stockwell
de0fd0556c fix: font measurement race + updater download URL mismatch
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m7s
Two fixes:

1. Terminal font rendering: xterm.js was calling fitAddon.fit() before
   fonts loaded. canvas.measureText() used a fallback font, got wrong
   cell dimensions (2-3px per char instead of 8-9px), producing 200+
   column terminals where text appeared as tiny dashes with colored
   blocks. Fixed by waiting for document.fonts.ready before fitting.
   Also prioritized Windows-native fonts (Cascadia Mono, Consolas)
   in the font stack.

2. Updater download URL: tagVersion used raw release.TagName ("v0.8.3")
   but CI uploads packages under stripped version ("0.8.3"). Download
   URL was .../v0.8.3/wraith-v0.8.3-setup.exe but the actual package
   is at .../0.8.3/wraith-0.8.3-setup.exe. Now uses latestVer (stripped).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 14:22:53 -04:00
Vantz Stockwell
c3beb6df6b fix: revert CWD tracker from readLoop — corrupts ANSI escape sequences
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m4s
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>
2026-03-17 14:14:52 -04:00
Vantz Stockwell
bce77e0932 feat: wire SFTP CWD following — listen for OSC 7 events + inject shell hook
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m2s
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>
2026-03-17 14:00:24 -04:00
Vantz Stockwell
ea53ca42f0 fix: wire Settings update button — was a console.log stub
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m5s
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>
2026-03-17 13:54:43 -04:00
Vantz Stockwell
a6db3ddfa4 feat: fix 6 frontend issues (F-1, F-5, F-6, F-7, F-10, F-11)
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>
2026-03-17 13:41:28 -04:00
Vantz Stockwell
9d19147568 chore: remove unused deps, add connection_history migration, wire plugin registry
- 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>
2026-03-17 13:39:44 -04:00
Vantz Stockwell
68e3e38d75 feat(sftp): wire toolbar buttons and live transfer progress panel
- S-1 Upload: hidden file input, FileReader reads as text, calls SFTP.WriteFile, refreshes listing
- S-2 Download: calls SFTP.ReadFile, creates Blob, triggers browser download via temporary <a> element
- S-3 Delete: confirm() dialog, calls SFTP.Delete, clears selection, refreshes listing
- S-4 New Folder: prompt() dialog, calls SFTP.Mkdir with full path, refreshes listing
- S-5 Transfer Progress: new useTransfers composable (module-level singleton) tracks active
  transfers; TransferProgress consumes it directly — no prop threading required
- Added single-click selection state to file entries; download/delete buttons dim when no
  valid selection exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 13:39:28 -04:00
Vantz Stockwell
2ae628c858 feat: MobaXTerm-style clipboard — select to copy, right-click to paste
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m3s
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>
2026-03-17 13:32:57 -04:00
Vantz Stockwell
05776b7eb6 fix: streaming UTF-8 decoder + rAF write batching for terminal performance
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m6s
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>
2026-03-17 13:22:50 -04:00
Vantz Stockwell
9fce0b6c1e fix: UTF-8 terminal rendering — atob() decodes as Latin-1, not UTF-8
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m5s
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>
2026-03-17 13:00:29 -04:00
Vantz Stockwell
1d61b1faf2 refactor: remove Claude AI copilot panel — will be replaced with embedded terminal
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m10s
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>
2026-03-17 12:58:31 -04:00
Vantz Stockwell
e1517daf9a fix: updater download — pass full UpdateInfo object, not just version string
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m4s
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>
2026-03-17 12:51:18 -04:00
Vantz Stockwell
e916d5942b feat: "Use Claude Code Token" button — imports credentials.json as OAuth fallback
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m5s
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>
2026-03-17 12:48:30 -04:00
Vantz Stockwell
af629fa373 fix: add username field to SSH key credential form — was missing entirely
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m3s
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>
2026-03-17 12:15:08 -04:00
Vantz Stockwell
9338fef0c2 fix: auto-switch sidebar to SFTP on SSH connect + credential debug logging
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m4s
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>
2026-03-17 11:54:21 -04:00
Vantz Stockwell
8362a50460 fix: SSH terminal [Object Object] — Wails v3 Events.On receives event object, not raw data
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m6s
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>
2026-03-17 11:46:00 -04:00
Vantz Stockwell
16d105e1fb fix: add credential delete button + fix OAuth token exchange error display
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m4s
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>
2026-03-17 11:42:27 -04:00
Vantz Stockwell
901d9c257d fix: SSH key double-base64 encoding — PEM was corrupted during storage
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m3s
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>
2026-03-17 11:39:24 -04:00
Vantz Stockwell
163af456b4 fix: SSH password prompt on auth failure, version from Go backend, visible errors
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m5s
- 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>
2026-03-17 11:38:08 -04:00
Vantz Stockwell
b46c20b0d0 feat: wire all remaining stubs — settings, SFTP, RDP, credentials, FreeRDP callbacks
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m4s
Four-agent parallel deployment:

1. Settings persistence — all 5 settings wired to SettingsService.Set/Get,
   theme picker persists, update check calls real UpdateService, external
   links use Browser.OpenURL, SFTP file open/save calls real service,
   Quick Connect creates real connection + session, exit uses Wails quit

2. SSH key management — credential dropdown in ConnectionEditDialog,
   collapsible "Add New Credential" panel with password/SSH key modes,
   CredentialService proxied through WraithApp (vault-locked guard),
   new CreateSSHKeyCredential method for atomic key+credential creation

3. RDP frontend wiring — useRdp.ts calls real RDPGetFrame/SendMouse/
   SendKey/SendClipboard via Wails bindings, ConnectRDP on WraithApp
   resolves credentials and builds RDPConfig, session store handles
   RDP protocol, frame pipeline uses polling at 30fps

4. FreeRDP3 callback registration — PostConnect and BitmapUpdate callbacks
   via syscall.NewCallback, GDI mode for automatic frame decoding,
   freerdp_context_new() call added, settings/input/context pointers
   extracted from struct offsets, BGRA→RGBA channel swap in frame copy,
   event loop fixed to pass context not instance

11 files changed. Zero build errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 11:25:03 -04:00
Vantz Stockwell
df23cecdbd fix: wire connection CRUD, add Group+/Host+ buttons, fix vault stubs
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m5s
- 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>
2026-03-17 11:08:40 -04:00
Vantz Stockwell
8572e6e7ea fix: wire SSH/SFTP/terminal to real Go backend — kill all stubs
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>
2026-03-17 10:49:21 -04:00
Vantz Stockwell
e5c69106c5 fix: wire auto-updater to real Go backend — check, download, install via Wails bindings
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m3s
2026-03-17 10:30:09 -04:00
Vantz Stockwell
a1dce82d99 fix: wire vault persistence, connection loading, and MobaXterm import to real Go backend
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>
2026-03-17 10:27:50 -04:00
Vantz Stockwell
fbd2fd4f80 feat: wire real Claude API — OAuth login + live chat via Wails bindings
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m2s
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>
2026-03-17 10:22:07 -04:00
Vantz Stockwell
f22fbe14fa feat: auto-updater — check Gitea packages for new versions, download + install
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m5s
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>
2026-03-17 10:11:50 -04:00
Vantz Stockwell
473a25cf2a fix: wire settings modal, import dialog, and connection edit into the UI
- 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>
2026-03-17 10:03:17 -04:00
Vantz Stockwell
be868e8172 feat: AI copilot panel — chat UI with streaming, tool visualization, OAuth settings
Add the XO copilot right-side panel with:
- Pinia store (copilot.store.ts) managing panel state, messages, streaming, and token tracking
- useCopilot composable with mock streaming (30-100ms/word) and context-aware responses
- CopilotPanel: 320px collapsible panel with header, scrollable message list, and input area
- CopilotMessage: markdown rendering (bold, code, lists) with streaming cursor animation
- CopilotToolViz: color-coded tool call cards (green=terminal, yellow=SFTP, blue=RDP)
  with pending spinner, done checkmark, expandable input/result JSON
- CopilotSettings: model selector, token usage, clear history, connect/disconnect
- MainLayout integration: ghost icon toggle in toolbar, Ctrl+Shift+K shortcut,
  CSS slide transition, content area resizes (not overlay)

All Wails AIService bindings are TODOs with mock behavior. Pure Tailwind CSS,
no external markdown library.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:03:33 -04:00
Vantz Stockwell
8a096d7f7b Wraith v0.1.0 — Desktop SSH + RDP + SFTP Client
Some checks failed
Build & Sign Wraith / Build Windows + Sign (push) Has been cancelled
Go + Wails v3 + Vue 3 + SQLite + FreeRDP3 (purego)
183 tests, 76 source files, 9,910 lines of code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:19:29 -04:00