Add execCommand options to opt into keyboard-interactive auth and wire MFA only
for export-key flows, preserving non-interactive exec usage elsewhere.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Sync port forwarding rules
- Refactor `usePortForwardingState` to use a global store pattern, ensuring state consistency across the application.
- Integrate `usePortForwardingState` into `App.tsx` to retrieve and update port forwarding rules.
- Update `useAutoSync` in `App.tsx` to include `portForwardingRules` in the sync payload and handle incoming updates via `importRules`.
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>
* Normalize imported port forwarding statuses
* fix: correct indentation in usePortForwardingState.ts
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>
* Normalize imported port forwarding rule status
* Stabilize port forwarding rules for auto-sync
---------
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Added `CreateWorkspaceDialog` component for creating named workspaces with multiple hosts.
- Implemented `createWorkspaceWithHosts` in `useSessionState`.
- Integrated dialog into `App.tsx` and triggered from Quick Switcher.
- Updated `QuickSwitcher` logic to improve visibility of recent connections.
- Added i18n keys for the dialog.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Added `setDeviceName` method to `CloudSyncManager` to update state, persist to local storage, and notify listeners.
- Updated `useCloudSync` hook to expose the `setDeviceName` function from the manager.
- Ensures device name updates are correctly handled and persisted across sessions.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Refactored the host filtering logic in `useManagedSourceSync` to index hosts by `managedSourceId` before iterating through sources. This reduces the complexity from O(N*M) to O(N+M), where N is the number of sources and M is the number of hosts.
Benchmarks showed a ~74x speedup (from ~600ms to ~8ms) with 500 sources and 25,000 hosts.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Expose addExternalUpload and updateExternalUpload methods from useSftpState
- Add download task to transfer queue when starting stream download
- Update progress during download with transferred bytes, total, and speed
- Update task status on completion, failure, or cancellation
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
* feat: stream-based SFTP download for large files
- Add showSaveDialog API for native file save dialog
- Modify handleDownload to use streaming transfer for remote files
- Show save dialog first, then stream directly to disk
- Avoid loading entire file into memory
- Fallback to memory-based download for local files or when streaming unavailable
This fixes the issue where downloading large files would cause high memory
usage as the entire file was loaded into memory before saving.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
* feat: stream-based download for SftpView
- Add getSftpIdForConnection to useSftpState for accessing SFTP session IDs
- Modify handleDownloadFileForSide in useSftpViewFileOps to use streaming
- Pass showSaveDialog, startStreamTransfer to SftpView hook chain
- For remote SFTP files: show save dialog then stream directly to disk
- For local files: fallback to memory-based download
This extends the stream download optimization to SftpView (dual-pane browser),
not just SFTPModal.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
* fix: improve download task handling and cancellation
- Add per-task cancellation for downloads via onCancelTask prop
- Add i18n translation keys for download status messages
- Prevent duplicate error toasts with errorHandled flag
- Add bridge capability check (result === undefined)
- Make direction field required in TransferTask interface
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
---------
Co-authored-by: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Replace 3-option setting (ask/enabled/disabled) with boolean toggle
- Remove CompressedUploadDialog component - no more user prompts
- Auto-detect tar availability and silently fallback to regular upload
- Apply compressed upload setting to drag-and-drop uploads
- Fix upload progress updates for compressed uploads via callback
- Add i18n for upload phase labels (compressing/uploading/extracting)
- Fix empty folder handling - fallback to regular mkdir
- Preserve error message on failed upload tasks for UI display
- Fix fallback logic to only re-upload failed folders, not successful ones
- Handle cancellation during all phases (compression/transfer/extraction)
- Fix filename display to use explicit phase markers instead of substring match
- Fix Toggle onChange prop for settings to work correctly
- Use DropEntry.relativePath for correct drag-drop folder paths
- Dynamic extraction timeout based on archive size (60s base + 30s per 10MB)
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
Add clearAndRemoveSources function that clears multiple SSH config files
in parallel but removes all sources in a single atomic update. This
prevents race conditions where concurrent removals could re-add sources
that were already deleted.
When deleting a group path with multiple managed sources, the batch
removal is now used instead of Promise.all with individual removals.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
When multiple managed sources sync concurrently via Promise.all, each
sync was independently updating the managedSources array, causing race
conditions where one source's lastSyncedAt would be overwritten.
Now syncManagedSource returns success status, and lastSyncedAt updates
are batched in a single update after all syncs complete.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Keep preserved SSH config content outside managed block markers to
prevent data loss when first bringing existing config under management
- Always bracket IPv6 addresses in ProxyJump values regardless of port,
as OpenSSH requires brackets to disambiguate colon separators
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
When creating a managed source from an existing SSH config file without
Netcatty markers, the code was appending the managed block without removing
original Host entries. This left duplicate Host blocks, and since OpenSSH
uses the first matching block, edits via Netcatty wouldn't take effect.
Now uses mergeWithExistingSshConfig to filter out existing Host blocks
that match managed hostnames before wrapping with markers.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Filter duplicates on managed import and convert existing hosts to managed
- Trigger initial sync when prevHosts is empty but managed sources exist
- Update previousHostsRef even when no managed sources to maintain baseline
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
When a managed host references a jump host outside its managed source,
changes to that external jump host now trigger a sync. This ensures
ProxyJump entries stay up-to-date when jump hosts are edited.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
ProxyJump changes now trigger sync since hostChain.hostIds is compared
alongside other host fields like hostname, port, username, etc.
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
When syncing managed hosts to SSH config files, properly serialize the
hostChain to ProxyJump directive instead of just adding a comment.
- Add serializeJumpHost() to format jump host as [user@]host[:port]
- Add buildProxyJumpValue() to convert hostChain.hostIds to ProxyJump
- Pass allHosts to serializer for looking up jump host details
- Supports multiple jump hosts (comma-separated)
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
When deleting a managed group, clear the managed block in the SSH config
file before removing the source. This prevents stale host entries from
remaining in the file after the group is deleted.
- Add clearAndRemoveSource function to useManagedSourceSync
- Pass callback to VaultView and call it before removing managed sources
- Ensures SSH config files stay in sync when managed groups are deleted
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Add CompressedUploadDialog component to let users choose between compressed and regular transfer methods
- Implement compressUploadService for handling folder compression and extraction on the server
- Add compressUploadBridge to expose compression functionality to the renderer process
- Add sftpUseCompressedUpload setting with three modes: ask, enabled, disabled
- Add new upload progress states: compressing, extracting, scanning, completed
- Add i18n translations for upload dialog and settings in English and Chinese
- Update SFTP modal to support compressed upload workflow with progress tracking
- Add storage key for persisting compressed upload preference across sessions
- Significantly reduces transfer time for folders by using tar compression when available on server
Use a ref to access the latest managedSources when updating lastSyncedAt
after sync completes. This prevents overwriting concurrent changes made
while a sync was in flight (e.g., user unmanaging a source or importing
a new managed file).
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Use marker blocks to preserve existing SSH config directives (Match, Include, Host *, etc.)
- Only replace content between BEGIN/END NETCATTY MANAGED markers
- Update managedSources.groupName when groups are renamed or moved
- Prevents data loss for users with custom SSH config entries
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Add protocol to change detection to trigger sync when protocol changes
- Sanitize labels (remove spaces) when moving hosts to managed groups via drag/drop
- Prevent duplicate managed imports by checking if file is already managed
- Add i18n keys for already managed file warning
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Add pending sync tracking to process host changes that occur during sync
- Strip spaces from labels when host is/will be in a managed source group
- Remove unused FileSymlink import
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Fix managed host sync: add group field to change detection
- Auto-set managedSourceId when moving host to managed group
- Add 'managed' badge to managed hosts in VaultView
- Fix file path resolution for managed sources using webUtils
- Add managedSources prop to HostDetailsPanel for proper sync
- Restrict spaces in label for managed hosts
- Reorder HostDetailsPanel sections: General > Address > Port & Credentials
- Add debug logging for managed source sync troubleshooting
- Add tree view mode alongside existing grid and list views
- Implement hierarchical display of hosts organized by groups
- Add expand/collapse all controls with Chinese translations
- Support all sorting modes (A-Z, Z-A, newest, oldest) in tree view
- Persist expand/collapse state across view switches and app restarts
- Hide Groups section in tree view to avoid duplication
- Display ungrouped hosts at root level instead of "General" group
- Add missing delete group dialog with proper translations
- Maintain full functionality: search, filtering, drag-drop, context menus
Technical changes:
- Create HostTreeView component with TreeNode recursive structure
- Add useTreeExpandedState hook for persistent state management
- Extend ViewMode type to include "tree" option
- Add sortMode prop to enable dynamic sorting in tree structure
- Separate group tree logic for tree view vs other view modes
- Add comprehensive English and Chinese translations
Add the ability to duplicate an SSH session by right-clicking on a tab
and selecting "Copy Tab". This creates a new session with the same
connection parameters (host, port, protocol, etc.).
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace memory-based file uploads with stream transfers for large files
- Add uploadWithStreams and downloadWithStreams functions in transferBridge
- Fix cancel transfer by properly destroying streams instead of throwing
errors in callbacks (which corrupted SSH connection)
- Fix upload button not triggering upload by copying FileList before
clearing input (clearing input also clears FileList reference)
- Export getPathForFile utility for obtaining local file paths
- Add startStreamTransfer and cancelTransfer bridge methods
Co-Authored-By: Claude <noreply@anthropic.com>
- Change SFTP date format from ISO 8601 to readable YYYY-MM-DD HH:mm
- Fix lastModifiedFormatted to use formatDate instead of raw ISO string
- Hide "General" group at root level in vault (hosts already shown below)
- Fix General group filter to match hosts with empty/undefined group
- Exclude .github/** from ESLint (CI scripts don't need local linting)
Co-Authored-By: Claude <noreply@anthropic.com>
The previous fix only checked the direct parent task ID, which doesn't
work for deeply nested folders:
- Top-level task "A" (user cancels this)
- Child folder "B" with parentTaskId="A" (detected cancellation)
- Grandchild file "C" with parentTaskId="B" (NOT detected!)
Now we pass rootTaskId through all levels of transferDirectory and
transferFile calls, so all nested operations check against the
original top-level task ID.
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, clicking cancel only updated the task status but didn't
stop the actual file transfer operations. The transferFile and
transferDirectory functions would continue running.
Changes:
- Add cancelledTasksRef to track cancelled task IDs
- Check cancellation status at start of transferFile/transferDirectory
- Check cancellation status during directory iteration
- Add cancelled task ID to ref in cancelTransfer
- Handle cancellation errors gracefully in processTransfer
- Clean up cancelled IDs after 5s delay
Now cancelling a folder transfer actually stops the operation.
Co-Authored-By: Claude <noreply@anthropic.com>
The UploadBridge interface has a different parameter order than
NetcattyBridge for writeSftpBinaryWithProgress:
- UploadBridge: (sftpId, path, data, taskId, onProgress, ...)
- NetcattyBridge: (sftpId, path, content, transferId, encoding, onProgress, ...)
Previously, the bridge was passed directly which caused callbacks
to be passed where encoding was expected, leading to
"An object could not be cloned" errors in Electron IPC.
Now we wrap the function to properly adapt the interfaces.
Co-Authored-By: Claude <noreply@anthropic.com>
When connecting to a new host, the pane's filenameEncoding was being
preserved from the previous connection. This caused issues when:
1. A pane was set to explicit encoding (e.g., UTF-8)
2. User connects to a host configured for GB18030
3. The bridge wouldn't auto-detect because encoding wasn't "auto"
Now the encoding is reset to the host's configured sftpEncoding or
"auto" when a new connection is established, ensuring proper
auto-detection and respecting host-level encoding settings.
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix temporal dead zone issue in useSftpTransfers.ts by moving
sourceEncoding declaration before its usage (P1 issue)
- Initialize filenameEncoding from host.sftpEncoding in SFTPModal.tsx
and add useEffect to update when host changes (P2 issue)
- Fix duplicate Host import in SftpModalHeader.tsx
Co-Authored-By: Claude <noreply@anthropic.com>
Merged the latest changes from main including:
- Bundled folder uploads with UploadController
- Fast delete using SSH exec
- UI improvements and bug fixes
Resolved conflicts in:
- useSftpExternalOperations.ts
- SFTPModal.tsx
- sftpBridge.cjs
Co-Authored-By: Claude <noreply@anthropic.com>
Adopts a "fail-fast" strategy for SFTP uploads, stopping the entire transfer process upon encountering any error during file or directory operations.
Introduces a new `isFatalUploadError` helper to consolidate and expand the definition of errors that should halt an upload, now explicitly including cases where the target directory is deleted or inaccessible during the transfer.
Removes specific fatal error checks from various components, streamlining error propagation and simplifying the overall error handling logic.
Ensures "Scanning files..." placeholder tasks are consistently removed when actual upload tasks are added, preventing potential state inconsistencies.
Refactors the SFTP upload mechanism to provide a more unified and robust experience.
- **Bundles folder uploads**: When uploading a folder from the local machine, it now appears as a single, aggregated task in the UI, showing overall progress instead of individual files.
- **Enhances cancellation**: Implements a new upload service and controller to manage transfers, allowing for more immediate and reliable cancellation of both individual files and bundled folder uploads.
- **Improves UI feedback**: Adds dedicated buttons for cancelling active uploads and dismissing completed, failed, or cancelled tasks.
- **Faster folder deletion**: Utilizes SSH `rm -rf` command for rapid remote folder deletion, falling back to SFTP rmdir if SSH exec is unavailable.
- Updates internationalization keys for single item deletion confirmation.
- Bundle folder uploads as single tasks showing aggregate progress
- Add unique file transfer IDs for proper cancellation tracking
- Fix cancel button to call cancelExternalUpload for external uploads
- Improve backend cancel detection using cancelled flag instead of error message
- Use SSH exec with rm -rf for fast folder deletion on remote servers
- Add FolderUp icon for folder upload tasks in transfer queue
- Add i18n key for upload cancelled message
Co-Authored-By: Claude <noreply@anthropic.com>
Adjusts relative import paths across the application to reflect recent changes in the project's directory structure. This primarily affects SFTP state management and modal components.
Updates React hook dependency arrays for `useCallback` and `useEffect` to ensure proper memoization and re-execution, addressing `react-hooks/exhaustive-deps` linting rules.
Improves type safety for translation function parameters by changing `Record<string, any>` to `Record<string, unknown>`.
Adds the `ls` command to the allowed commands for the Claude AI assistant in local settings.
Decomposes the core SFTP state management hook and related UI components into smaller, domain-specific custom hooks and specialized sub-components.
This refactoring aims to:
- Break down the monolithic `useSftpState` hook into focused hooks like `useSftpConnections`, `useSftpTransfers`, and `useSftpPaneActions`.
- Extract complex logic and rendering from `SFTPModal` and `SftpView` into dedicated presentational components and custom hooks.
- Centralize SFTP-related types and utility functions into new `sftp/types.ts` and `sftp/utils.ts` modules.
The changes enhance code organization, maintainability, and testability across the SFTP functionality.
Add real-time monitoring of Linux server resources in the terminal statusbar:
- CPU usage with per-core breakdown in hover popup
- Memory usage with htop-style colored bar (Used/Buffers/Cache/Free)
- Top 10 processes by memory consumption
- Disk usage for all mounted partitions
- Network speed (RX/TX) with per-interface details
Features:
- Only enabled for Linux servers (detected automatically)
- Configurable refresh interval (default 5 seconds)
- Toggle in Settings > Terminal tab
- Hover-triggered popups using Radix UI HoverCard
- BusyBox compatibility with fallback commands
Closes#108
Co-Authored-By: Claude <noreply@anthropic.com>
- Move autoStartExecutedRef assignment before async work to ensure
effect only runs once regardless of auto-start rules
- Memoize keys array in App.tsx to prevent new references on each render
Co-Authored-By: Claude <noreply@anthropic.com>
- Add manual export button in LogView to save session logs to file
- Add auto-save settings in System tab to automatically save logs when sessions end
- Support three formats: plain text (.txt), raw ANSI (.log), and HTML (.html)
- Logs are organized by host in subdirectories with timestamp filenames
- Add i18n support for English and Chinese
Closes#104
Co-Authored-By: Claude <noreply@anthropic.com>
Introduces a `useUIFontsLoaded` hook to track the loading status of UI fonts.
Updates the `useLayoutEffect` responsible for applying the UI font family to re-run once fonts are loaded. This ensures that local fonts, whose family properties might only be correctly resolved after loading, are applied accurately to the UI.
Also updates local Claude settings to allow GitHub CLI commands for development convenience.
Refactors UI font handling to support dynamic font loading and improves selection in settings.
Implements "Load More" pagination for connection logs, enhancing performance and user experience with large datasets. Adds lazy loading for the Connection Logs Manager.
Addresses an issue in `usePortForwardingState` to prevent duplicate backend sync calls in React StrictMode. Improves IPC communication robustness by checking window destruction status. Refines the visual presentation of the snippets empty state.
- Add UI fonts configuration with 21 font options
- Add state management for UI font preference
- Add i18n translations (English/Chinese)
- Apply font via CSS variable --font-sans
- Support cross-window sync via IPC/localStorage
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com>
- Auto-discover and use default SSH keys (~/.ssh/id_ed25519, id_ecdsa, id_rsa) when no credentials configured
- Add Windows SSH Agent service status detection with UI warning
- Remove file extension filter on key import to allow any file type
- Unify Host Details panel card styles with consistent icons and titles
Co-Authored-By: Claude <noreply@anthropic.com>
Enhances SFTP file upload performance and responsiveness by:
- Throttling UI progress updates with `requestAnimationFrame` to reduce re-renders.
- Optimizing Electron-side binary writes with larger chunk sizes, `subarray` for buffers, and efficient buffer handling.
- Implementing IPC progress event throttling to reduce communication overhead between main and renderer processes.
- Speeding up folder processing by traversing directory entries in parallel batches, with yielding to maintain UI responsiveness.
Improves robustness by:
- Ensuring folder uploads stop immediately upon user cancellation.
- Preventing division-by-zero errors in the progress bar calculation.
Also updates Claude settings to allow `npm run build:*` commands.