fix: restore RFC SSH defaults for dockerized dev sessions

linuztx committed Mar 8, 2026 at 19:28 UTC 7da508d1cd74a8930b73925bc390e4b32c9df1f9
3 files changed +62 -25
plugins/code_execution/default_config.yaml
+8 -5
@@ -1,10 +1,13 @@
1 -# Shell interface: false = local TTY, true = SSH
2 -ssh_enabled: false
1 +# Shell interface: auto = auto-detect (SSH when non-dockerized, local when dockerized),
2 +# true = always SSH, false = always local TTY
3 +ssh_enabled: auto
4
4 -# SSH credentials (used when ssh_enabled is true)
5 -ssh_addr: localhost
6 -ssh_port: 22
5 +# SSH credentials (used when ssh_enabled is true or auto-detected)
6 +# ssh_addr: derived from rfc_url setting when empty
7 +ssh_addr: ""
8 +ssh_port: 55022
9 ssh_user: root
10 +# ssh_pass: uses root_password from RFC exchange when empty
11 ssh_pass: ""
12
13 # Timeouts for python/nodejs/terminal runtimes (seconds)
plugins/code_execution/tools/code_execution_tool.py
+39 -7
@@ -5,7 +5,7 @@ import shlex
5 import time
6
7 from helpers.tool import Tool, Response
8 -from helpers import files, projects, runtime, settings
8 +from helpers import files, rfc_exchange, projects, runtime, settings
9 from helpers.print_style import PrintStyle
10 from helpers.strings import truncate_text as truncate_text_string
11 from helpers.messages import truncate_text as truncate_text_agent
@@ -15,15 +15,46 @@ from plugins.code_execution.helpers.shell_local import LocalInteractiveSession
15 from plugins.code_execution.helpers.shell_ssh import SSHInteractiveSession
16
17
18 +def _resolve_ssh_enabled(raw_value) -> bool:
19 + """Resolve ssh_enabled: 'auto' detects based on dockerized state."""
20 + val = str(raw_value).strip().lower()
21 + if val == "auto":
22 + return not runtime.is_dockerized()
23 + return val in ("true", "1", "yes", "on")
24 +
25 +
26 +def _resolve_ssh_addr(cfg_addr: str) -> str:
27 + """Resolve SSH address: fall back to rfc_url from settings when empty."""
28 + if cfg_addr:
29 + return cfg_addr
30 + set = settings.get_settings()
31 + host = set.get("rfc_url", "localhost")
32 + # Strip protocol and port from URL
33 + if "//" in host:
34 + host = host.split("//")[1]
35 + if ":" in host:
36 + host = host.split(":")[0]
37 + if host.endswith("/"):
38 + host = host.rstrip("/")
39 + return host or "localhost"
40 +
41 +
42 +async def _resolve_ssh_pass(cfg_pass: str) -> str:
43 + """Resolve SSH password: fall back to root_password via RFC when empty."""
44 + if cfg_pass:
45 + return cfg_pass
46 + return await rfc_exchange.get_root_password()
47 +
48 +
49 def _get_config(agent) -> dict:
50 cfg = plugins.get_plugin_config("code_execution", agent=agent) or {}
51
21 - # SSH / TTY switch
22 - ssh_enabled = bool(cfg.get("ssh_enabled", False))
52 + # SSH / TTY switch (supports auto/true/false)
53 + ssh_enabled = _resolve_ssh_enabled(cfg.get("ssh_enabled", "auto"))
54
24 - # SSH credentials
25 - ssh_addr = str(cfg.get("ssh_addr", "localhost"))
26 - ssh_port = int(cfg.get("ssh_port", 22))
55 + # SSH credentials (with RFC fallbacks)
56 + ssh_addr = _resolve_ssh_addr(str(cfg.get("ssh_addr", "")))
57 + ssh_port = int(cfg.get("ssh_port", 55022))
58 ssh_user = str(cfg.get("ssh_user", "root"))
59 ssh_pass = str(cfg.get("ssh_pass", ""))
60
@@ -186,12 +217,13 @@ class CodeExecution(Tool):
217 if session is not None and session not in shells:
218 cwd = await self.ensure_cwd()
219 if ssh_enabled:
220 + ssh_pass = await _resolve_ssh_pass(cfg["ssh_pass"])
221 shell = SSHInteractiveSession(
222 self.agent.context.log,
223 cfg["ssh_addr"],
224 cfg["ssh_port"],
225 cfg["ssh_user"],
194 - cfg["ssh_pass"],
226 + ssh_pass,
227 cwd=cwd,
228 )
229 else:
plugins/code_execution/webui/config.html
+15 -13
@@ -15,36 +15,38 @@
15 <!-- Shell Interface -->
16 <div class="field">
17 <div class="field-label">
18 - <div class="field-title">Use SSH</div>
18 + <div class="field-title">Shell Interface</div>
19 <div class="field-description">
20 - When enabled, code execution connects via SSH using the credentials below. When disabled, a local Python TTY is used.
20 + <b>Auto</b>: SSH when running outside Docker, local TTY when dockerized.
21 + <b>SSH</b>: always connect via SSH. <b>Local</b>: always use local PTY.
22 </div>
23 </div>
24 <div class="field-control">
24 - <label class="toggle">
25 - <input type="checkbox" x-model="$store.pluginSettings.settings.ssh_enabled" />
26 - <span class="toggler"></span>
27 - </label>
25 + <select x-model="$store.pluginSettings.settings.ssh_enabled">
26 + <option value="auto">Auto</option>
27 + <option value="true">SSH</option>
28 + <option value="false">Local TTY</option>
29 + </select>
30 </div>
31 </div>
32
31 - <!-- SSH credentials (shown only when SSH is enabled) -->
32 - <template x-if="$store.pluginSettings.settings.ssh_enabled">
33 + <!-- SSH credentials (shown when SSH or Auto) -->
34 + <template x-if="$store.pluginSettings.settings.ssh_enabled !== 'false'">
35 <div>
36 <div class="field">
37 <div class="field-label">
38 <div class="field-title">SSH Address</div>
37 - <div class="field-description">Hostname or IP of the SSH target.</div>
39 + <div class="field-description">Hostname or IP of the SSH target. Leave empty to use the RFC URL from settings.</div>
40 </div>
41 <div class="field-control">
40 - <input type="text" x-model="$store.pluginSettings.settings.ssh_addr" placeholder="localhost" />
42 + <input type="text" x-model="$store.pluginSettings.settings.ssh_addr" placeholder="(from RFC URL)" />
43 </div>
44 </div>
45
46 <div class="field">
47 <div class="field-label">
48 <div class="field-title">SSH Port</div>
47 - <div class="field-description">SSH port on the target host.</div>
49 + <div class="field-description">SSH port on the target host (default: 55022 for Docker RFC).</div>
50 </div>
51 <div class="field-control">
52 <input type="number" min="1" max="65535" x-model.number="$store.pluginSettings.settings.ssh_port" />
@@ -64,10 +66,10 @@
66 <div class="field">
67 <div class="field-label">
68 <div class="field-title">SSH Password</div>
67 - <div class="field-description">Password for SSH login.</div>
69 + <div class="field-description">Password for SSH login. Leave empty to use the dev root password (via RFC).</div>
70 </div>
71 <div class="field-control">
70 - <input type="password" autocomplete="off" x-model="$store.pluginSettings.settings.ssh_pass" />
72 + <input type="password" autocomplete="off" x-model="$store.pluginSettings.settings.ssh_pass" placeholder="(from RFC)" />
73 </div>
74 </div>
75 </div>