Skip to content

feat: add ProxyCommand support for SSH connections#1117

Merged
GT-610 merged 3 commits into
lollipopkit:mainfrom
GT-610:proxy-command
Apr 9, 2026
Merged

feat: add ProxyCommand support for SSH connections#1117
GT-610 merged 3 commits into
lollipopkit:mainfrom
GT-610:proxy-command

Conversation

@GT-610
Copy link
Copy Markdown
Collaborator

@GT-610 GT-610 commented Apr 9, 2026

Resolve #949.
Resolve #803.

Summary by CodeRabbit

  • New Features

    • Added SSH ProxyCommand support for routing SSH via user-specified proxy commands.
    • Server editor: new ProxyCommand input (desktop-only); submission blocked on unsupported platforms.
    • SSH config parsing now recognizes ProxyCommand and propagates it to server settings.
  • Bug Fixes / Validation

    • Prevent saving when both ProxyCommand and jump host are set; shows explanatory message.
  • Localization

    • Added UI strings for ProxyCommand support and jump-vs-proxy conflict.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Adds support for SSH ProxyCommand connections. Introduces ProxyCommandSocket which resolves %h/%p/%r/%%, wraps the command in a platform-appropriate shell, runs it as a subprocess, and exposes its stdio as an SSHSocket implementation with readiness, timeout, close, destroy, and error semantics. The SSH config parser now reads proxycommand into Spi.proxyCommand and validation prevents using proxyCommand together with jump-server configuration. Connection logic in genClient uses ProxyCommandSocket.connect(...) when a proxy command is configured. UI and persistence were extended to edit and store the proxyCommand.

Possibly related PRs

Suggested reviewers

  • lollipopkit
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add ProxyCommand support for SSH connections' clearly and concisely summarizes the main change: adding ProxyCommand functionality for SSH connections.
Linked Issues check ✅ Passed The PR successfully implements ProxyCommand support as required by #949 and #803, enabling SSH connections via proxy commands (e.g., cloudflared, HTTP proxies). Core infrastructure is present: ProxyCommandSocket implementation, SSH config parsing, UI input fields, validation, and i18n strings.
Out of Scope Changes check ✅ Passed All changes are directly related to ProxyCommand support: socket implementation, config parsing, UI additions, validation, serialization, and localization. No unrelated or out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot requested a review from lollipopkit April 9, 2026 13:49
devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/l10n/app_zh.arb`:
- Around line 277-280: The ARB keys added in app_zh.arb (e.g.,
"jumpServerAndProxyCommandCannotBeUsedTogether",
"proxyCommandOnlySupportedOnDesktop") are not reflected in the generated Dart
localization files; run `flutter gen-l10n` to regenerate the localization
outputs so the typed accessors and updated LocalizationsDelegates include these
keys, then add the generated Dart files to the repo (commit the files under the
lib/l10n output directory), and re-run tests/CI to ensure there are no
missing-localization errors; always regenerate (`flutter gen-l10n`) after
modifying any lib/l10n/*.arb.

In `@lib/view/page/server/edit/actions.dart`:
- Around line 69-72: Trim the proxy command text before checking desktop gating
and before saving: replace uses of _proxyCommandCtrl.text.isNotEmpty with
_proxyCommandCtrl.text.trim().isNotEmpty (and use trimmed value when assigning
proxyCommand) so whitespace-only input is treated as empty; apply the same
change for the other occurrence around the save logic at the second check (the
block at ~120) to ensure both gating and persistence use the trimmed string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 63e6ccde-85e8-4c3c-8130-2e46fa77cb42

📥 Commits

Reviewing files that changed from the base of the PR and between 974310d and 2e5fd0f.

⛔ Files ignored due to path filters (15)
  • lib/generated/l10n/l10n.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_de.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_en.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_es.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_fr.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_id.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_it.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_ja.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_ko.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_nl.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_pt.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_ru.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_tr.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_uk.dart is excluded by !**/generated/**
  • lib/generated/l10n/l10n_zh.dart is excluded by !**/generated/**
📒 Files selected for processing (8)
  • lib/core/utils/proxy_command_socket.dart
  • lib/core/utils/server.dart
  • lib/core/utils/ssh_config.dart
  • lib/data/model/server/server_private_info.dart
  • lib/data/provider/server/all.dart
  • lib/l10n/app_en.arb
  • lib/l10n/app_zh.arb
  • lib/view/page/server/edit/actions.dart
✅ Files skipped from review due to trivial changes (1)
  • lib/l10n/app_en.arb
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/core/utils/ssh_config.dart
  • lib/core/utils/proxy_command_socket.dart

Comment thread lib/l10n/app_zh.arb
Comment thread lib/view/page/server/edit/actions.dart Outdated
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 new potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment thread lib/core/utils/ssh_config.dart
Comment thread lib/core/utils/ssh_config.dart
Comment thread lib/core/utils/server.dart
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/data/model/server/server_private_info.dart (1)

85-87: Avoid hard-coded English exception text in the model layer.

This literal should be represented as a typed/code error and localized when rendered, instead of embedding a fixed English message here.

♻️ Proposed refactor
+class SpiValidationException implements Exception {
+  final SpiValidationError error;
+  const SpiValidationException(this.error);
+}
+
 extension Spix on Spi {
   void validateOrThrow() {
     final validationError = validate();
     if (validationError == null) return;
-    switch (validationError) {
-      case SpiValidationError.jumpServerAndProxyCommandConflict:
-        throw ArgumentError(
-          'Jump server and ProxyCommand cannot be used together.',
-        );
-    }
+    throw SpiValidationException(validationError);
   }
 }

As per coding guidelines lib/**/*.dart: "Use libL10n and l10n for localization strings, prioritizing libL10n from fl_lib package to avoid duplication".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/model/server/server_private_info.dart` around lines 85 - 87, Replace
the hard-coded English ArgumentError message in server_private_info.dart with a
typed error and a localization key: define/throw a specific exception (e.g.,
JumpServerProxyCommandConflict or ArgumentError with a distinct errorCode field)
from the same module instead of the literal string, and move the human-readable
message into libL10n/l10n (prefer libL10n from fl_lib) so UI code can localize
it when rendering; update any callers/tests to check the typed error or
errorCode rather than the exact English text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/data/model/server/server_private_info.dart`:
- Around line 85-87: Replace the hard-coded English ArgumentError message in
server_private_info.dart with a typed error and a localization key: define/throw
a specific exception (e.g., JumpServerProxyCommandConflict or ArgumentError with
a distinct errorCode field) from the same module instead of the literal string,
and move the human-readable message into libL10n/l10n (prefer libL10n from
fl_lib) so UI code can localize it when rendering; update any callers/tests to
check the typed error or errorCode rather than the exact English text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8a4bc23a-2e78-4a84-b145-20b25cbf3616

📥 Commits

Reviewing files that changed from the base of the PR and between 2e5fd0f and bb9c2db.

📒 Files selected for processing (3)
  • lib/core/utils/proxy_command_socket.dart
  • lib/data/model/server/server_private_info.dart
  • lib/view/page/server/edit/actions.dart
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/view/page/server/edit/actions.dart
  • lib/core/utils/proxy_command_socket.dart

@GT-610
Copy link
Copy Markdown
Collaborator Author

GT-610 commented Apr 9, 2026

🧹 Nitpick comments (1)

lib/data/model/server/server_private_info.dart (1)> 85-87: Avoid hard-coded English exception text in the model layer.

This literal should be represented as a typed/code error and localized when rendered, instead of embedding a fixed English message here.

♻️ Proposed refactor

+class SpiValidationException implements Exception {
+  final SpiValidationError error;
+  const SpiValidationException(this.error);
+}
+
 extension Spix on Spi {
   void validateOrThrow() {
     final validationError = validate();
     if (validationError == null) return;
-    switch (validationError) {
-      case SpiValidationError.jumpServerAndProxyCommandConflict:
-        throw ArgumentError(
-          'Jump server and ProxyCommand cannot be used together.',
-        );
-    }
+    throw SpiValidationException(validationError);
   }
 }

As per coding guidelines lib/**/*.dart: "Use libL10n and l10n for localization strings, prioritizing libL10n from fl_lib package to avoid duplication".

🤖 Prompt for AI Agents

Verify each finding against the current code and only fix it if needed.

In `@lib/data/model/server/server_private_info.dart` around lines 85 - 87, Replace
the hard-coded English ArgumentError message in server_private_info.dart with a
typed error and a localization key: define/throw a specific exception (e.g.,
JumpServerProxyCommandConflict or ArgumentError with a distinct errorCode field)
from the same module instead of the literal string, and move the human-readable
message into libL10n/l10n (prefer libL10n from fl_lib) so UI code can localize
it when rendering; update any callers/tests to check the typed error or
errorCode rather than the exact English text.

🤖 Prompt for all review comments with AI agents

Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/data/model/server/server_private_info.dart`:
- Around line 85-87: Replace the hard-coded English ArgumentError message in
server_private_info.dart with a typed error and a localization key: define/throw
a specific exception (e.g., JumpServerProxyCommandConflict or ArgumentError with
a distinct errorCode field) from the same module instead of the literal string,
and move the human-readable message into libL10n/l10n (prefer libL10n from
fl_lib) so UI code can localize it when rendering; update any callers/tests to
check the typed error or errorCode rather than the exact English text.

ℹ️ Review info

No issue now: the hard-coded English ArgumentError message in server_private_info.dart has already been replaced with a typed SpiValidationException, and UI rendering is localized in actions.dart.

@GT-610 GT-610 merged commit f9e27db into lollipopkit:main Apr 9, 2026
2 checks passed
@GT-610 GT-610 deleted the proxy-command branch April 9, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add: cloudflared/Cloudflare Tunnel support 功能请求:增加使用HTTP代理连接SSH

1 participant