refactor: move private helpers below class and add section comment

linuztx committed Mar 8, 2026 at 19:39 UTC 2c7ad353c754c7565d9034aba16dacdb4d81d4ce
1 file changed +110 -106
plugins/code_execution/tools/code_execution_tool.py
+110 -106
@@ -15,111 +15,6 @@ 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 -
52 - # SSH / TTY switch (supports auto/true/false)
53 - ssh_enabled = _resolve_ssh_enabled(cfg.get("ssh_enabled", "auto"))
54 -
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 -
61 - # Timeouts for python/nodejs/terminal runtimes
62 - code_exec_timeouts = {
63 - "first_output_timeout": int(cfg.get("code_exec_first_output_timeout", 30)),
64 - "between_output_timeout": int(cfg.get("code_exec_between_output_timeout", 15)),
65 - "max_exec_timeout": int(cfg.get("code_exec_max_exec_timeout", 180)),
66 - "dialog_timeout": int(cfg.get("code_exec_dialog_timeout", 5)),
67 - }
68 -
69 - # Timeouts for "output" runtime
70 - output_timeouts = {
71 - "first_output_timeout": int(cfg.get("output_first_output_timeout", 90)),
72 - "between_output_timeout": int(cfg.get("output_between_output_timeout", 45)),
73 - "max_exec_timeout": int(cfg.get("output_max_exec_timeout", 300)),
74 - "dialog_timeout": int(cfg.get("output_dialog_timeout", 5)),
75 - }
76 -
77 - # Prompt patterns (one regex per line, or a list)
78 - prompt_patterns_raw = cfg.get(
79 - "prompt_patterns",
80 - r"(\(venv\)).+[$#] ?$" + "\n"
81 - + r"root@[^:]+:[^#]+# ?$" + "\n"
82 - + r"[a-zA-Z0-9_.-]+@[^:]+:[^$#]+[$#] ?$" + "\n"
83 - + r"\(?.*\)?\s*PS\s+[^>]+> ?$",
84 - )
85 - if isinstance(prompt_patterns_raw, list):
86 - prompt_lines = [str(p) for p in prompt_patterns_raw]
87 - else:
88 - prompt_lines = str(prompt_patterns_raw).splitlines()
89 - prompt_patterns = [
90 - re.compile(p.strip())
91 - for p in prompt_lines
92 - if p.strip()
93 - ]
94 -
95 - # Dialog patterns (one regex per line, or a list)
96 - dialog_patterns_raw = cfg.get(
97 - "dialog_patterns",
98 - "Y/N\nyes/no\n:\\s*$\n\\?\\s*$",
99 - )
100 - if isinstance(dialog_patterns_raw, list):
101 - dialog_lines = [str(p) for p in dialog_patterns_raw]
102 - else:
103 - dialog_lines = str(dialog_patterns_raw).splitlines()
104 - dialog_patterns = [
105 - re.compile(p.strip(), re.IGNORECASE)
106 - for p in dialog_lines
107 - if p.strip()
108 - ]
109 -
110 - return {
111 - "ssh_enabled": ssh_enabled,
112 - "ssh_addr": ssh_addr,
113 - "ssh_port": ssh_port,
114 - "ssh_user": ssh_user,
115 - "ssh_pass": ssh_pass,
116 - "code_exec_timeouts": code_exec_timeouts,
117 - "output_timeouts": output_timeouts,
118 - "prompt_patterns": prompt_patterns,
119 - "dialog_patterns": dialog_patterns,
120 - }
121 -
122 -
18 @dataclass
19 class ShellWrap:
20 id: int
@@ -477,7 +372,7 @@ class CodeExecution(Tool):
372 truncated_output.splitlines()[-3:] if truncated_output else []
373 )
374 last_lines.reverse()
480 - for idx, line in enumerate(last_lines):
375 + for _, line in enumerate(last_lines):
376 for pat in prompt_patterns:
377 if pat.search(line.strip()):
378 PrintStyle.info(
@@ -564,6 +459,115 @@ class CodeExecution(Tool):
459 return normalized
460
461
462 +# ------------------------------------------------------------------
463 +# Internal
464 +# ------------------------------------------------------------------
465 +
466 +def _resolve_ssh_enabled(raw_value) -> bool:
467 + """Resolve ssh_enabled: 'auto' detects based on dockerized state."""
468 + val = str(raw_value).strip().lower()
469 + if val == "auto":
470 + return not runtime.is_dockerized()
471 + return val in ("true", "1", "yes", "on")
472 +
473 +
474 +def _resolve_ssh_addr(cfg_addr: str) -> str:
475 + """Resolve SSH address: fall back to rfc_url from settings when empty."""
476 + if cfg_addr:
477 + return cfg_addr
478 + set = settings.get_settings()
479 + host = set.get("rfc_url", "localhost")
480 + # Strip protocol and port from URL
481 + if "//" in host:
482 + host = host.split("//")[1]
483 + if ":" in host:
484 + host = host.split(":")[0]
485 + if host.endswith("/"):
486 + host = host.rstrip("/")
487 + return host or "localhost"
488 +
489 +
490 +async def _resolve_ssh_pass(cfg_pass: str) -> str:
491 + """Resolve SSH password: fall back to root_password via RFC when empty."""
492 + if cfg_pass:
493 + return cfg_pass
494 + return await rfc_exchange.get_root_password()
495 +
496 +
497 +def _get_config(agent) -> dict:
498 + cfg = plugins.get_plugin_config("code_execution", agent=agent) or {}
499 +
500 + # SSH / TTY switch (supports auto/true/false)
501 + ssh_enabled = _resolve_ssh_enabled(cfg.get("ssh_enabled", "auto"))
502 +
503 + # SSH credentials (with RFC fallbacks)
504 + ssh_addr = _resolve_ssh_addr(str(cfg.get("ssh_addr", "")))
505 + ssh_port = int(cfg.get("ssh_port", 55022))
506 + ssh_user = str(cfg.get("ssh_user", "root"))
507 + ssh_pass = str(cfg.get("ssh_pass", ""))
508 +
509 + # Timeouts for python/nodejs/terminal runtimes
510 + code_exec_timeouts = {
511 + "first_output_timeout": int(cfg.get("code_exec_first_output_timeout", 30)),
512 + "between_output_timeout": int(cfg.get("code_exec_between_output_timeout", 15)),
513 + "max_exec_timeout": int(cfg.get("code_exec_max_exec_timeout", 180)),
514 + "dialog_timeout": int(cfg.get("code_exec_dialog_timeout", 5)),
515 + }
516 +
517 + # Timeouts for "output" runtime
518 + output_timeouts = {
519 + "first_output_timeout": int(cfg.get("output_first_output_timeout", 90)),
520 + "between_output_timeout": int(cfg.get("output_between_output_timeout", 45)),
521 + "max_exec_timeout": int(cfg.get("output_max_exec_timeout", 300)),
522 + "dialog_timeout": int(cfg.get("output_dialog_timeout", 5)),
523 + }
524 +
525 + # Prompt patterns (one regex per line, or a list)
526 + prompt_patterns_raw = cfg.get(
527 + "prompt_patterns",
528 + r"(\(venv\)).+[$#] ?$" + "\n"
529 + + r"root@[^:]+:[^#]+# ?$" + "\n"
530 + + r"[a-zA-Z0-9_.-]+@[^:]+:[^$#]+[$#] ?$" + "\n"
531 + + r"\(?.*\)?\s*PS\s+[^>]+> ?$",
532 + )
533 + if isinstance(prompt_patterns_raw, list):
534 + prompt_lines = [str(p) for p in prompt_patterns_raw]
535 + else:
536 + prompt_lines = str(prompt_patterns_raw).splitlines()
537 + prompt_patterns = [
538 + re.compile(p.strip())
539 + for p in prompt_lines
540 + if p.strip()
541 + ]
542 +
543 + # Dialog patterns (one regex per line, or a list)
544 + dialog_patterns_raw = cfg.get(
545 + "dialog_patterns",
546 + "Y/N\nyes/no\n:\\s*$\n\\?\\s*$",
547 + )
548 + if isinstance(dialog_patterns_raw, list):
549 + dialog_lines = [str(p) for p in dialog_patterns_raw]
550 + else:
551 + dialog_lines = str(dialog_patterns_raw).splitlines()
552 + dialog_patterns = [
553 + re.compile(p.strip(), re.IGNORECASE)
554 + for p in dialog_lines
555 + if p.strip()
556 + ]
557 +
558 + return {
559 + "ssh_enabled": ssh_enabled,
560 + "ssh_addr": ssh_addr,
561 + "ssh_port": ssh_port,
562 + "ssh_user": ssh_user,
563 + "ssh_pass": ssh_pass,
564 + "code_exec_timeouts": code_exec_timeouts,
565 + "output_timeouts": output_timeouts,
566 + "prompt_patterns": prompt_patterns,
567 + "dialog_patterns": dialog_patterns,
568 + }
569 +
570 +
571 def make_dir(path: str):
572 import os
573 os.makedirs(path, exist_ok=True)