portacode 0.3.3.dev0__tar.gz → 0.3.4.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/PKG-INFO +1 -1
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/_version.py +2 -2
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/session.py +8 -92
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/.gitignore +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/.gitmodules +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/LICENSE +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/MANIFEST.in +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/Makefile +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/README.md +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/docker-compose.yaml +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/README.md +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/__init__.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/__main__.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/cli.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/README.md +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/client.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/terminal.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/data.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/keypair.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/service.py +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode.egg-info/SOURCES.txt +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/pyproject.toml +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/setup.cfg +0 -0
- {portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/setup.py +0 -0
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.3.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
20
|
+
__version__ = version = '0.3.4.dev'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 3, 4, 'dev0')
|
|
@@ -10,14 +10,6 @@ from pathlib import Path
|
|
|
10
10
|
from typing import Any, Dict, Optional, List, TYPE_CHECKING
|
|
11
11
|
from collections import deque
|
|
12
12
|
|
|
13
|
-
# Terminal control imports (only available on Unix)
|
|
14
|
-
try:
|
|
15
|
-
import termios
|
|
16
|
-
import tty
|
|
17
|
-
_HAS_TERMIOS = True
|
|
18
|
-
except ImportError:
|
|
19
|
-
_HAS_TERMIOS = False
|
|
20
|
-
|
|
21
13
|
if TYPE_CHECKING:
|
|
22
14
|
from ..multiplex import Channel
|
|
23
15
|
|
|
@@ -29,15 +21,6 @@ _IS_WINDOWS = sys.platform.startswith("win")
|
|
|
29
21
|
_DEFAULT_ENV = {
|
|
30
22
|
"TERM": "xterm-256color",
|
|
31
23
|
"LANG": "C.UTF-8",
|
|
32
|
-
# Add additional terminal environment variables for better compatibility
|
|
33
|
-
"COLUMNS": "80",
|
|
34
|
-
"LINES": "24",
|
|
35
|
-
"PS1": r'\u@\h:\w\$ ', # Standard bash prompt
|
|
36
|
-
"HISTSIZE": "1000",
|
|
37
|
-
"HISTFILESIZE": "2000",
|
|
38
|
-
# Ensure proper terminal behavior
|
|
39
|
-
"DEBIAN_FRONTEND": "noninteractive", # Prevent interactive prompts
|
|
40
|
-
"TERM_PROGRAM": "portacode",
|
|
41
24
|
}
|
|
42
25
|
|
|
43
26
|
|
|
@@ -46,12 +29,6 @@ def _build_child_env() -> Dict[str, str]:
|
|
|
46
29
|
env = os.environ.copy()
|
|
47
30
|
for k, v in _DEFAULT_ENV.items():
|
|
48
31
|
env.setdefault(k, v)
|
|
49
|
-
|
|
50
|
-
# Ensure SSH_TTY is not set when using pipes to avoid shell confusion
|
|
51
|
-
if "SSH_TTY" in env:
|
|
52
|
-
logger.debug("Removing SSH_TTY from environment to prevent shell confusion")
|
|
53
|
-
del env["SSH_TTY"]
|
|
54
|
-
|
|
55
32
|
return env
|
|
56
33
|
|
|
57
34
|
|
|
@@ -213,19 +190,17 @@ class SessionManager:
|
|
|
213
190
|
channel_id = self._allocate_channel_id()
|
|
214
191
|
channel = self.mux.get_channel(channel_id)
|
|
215
192
|
|
|
216
|
-
# Choose shell - prefer bash over sh for better compatibility
|
|
193
|
+
# Choose shell - prefer bash over sh for better terminal compatibility
|
|
217
194
|
if shell is None:
|
|
218
195
|
if not _IS_WINDOWS:
|
|
219
|
-
# Try to use bash if available, fallback to default shell
|
|
220
196
|
shell = os.getenv("SHELL")
|
|
221
|
-
|
|
222
|
-
|
|
197
|
+
# If the default shell is /bin/sh, try to use bash instead for better terminal support
|
|
198
|
+
if shell == "/bin/sh":
|
|
223
199
|
for bash_path in ["/bin/bash", "/usr/bin/bash", "/usr/local/bin/bash"]:
|
|
224
200
|
if os.path.exists(bash_path):
|
|
225
201
|
shell = bash_path
|
|
202
|
+
logger.info("Switching from /bin/sh to %s for better terminal compatibility", shell)
|
|
226
203
|
break
|
|
227
|
-
else:
|
|
228
|
-
shell = "/bin/sh"
|
|
229
204
|
else:
|
|
230
205
|
shell = os.getenv("COMSPEC", "cmd.exe")
|
|
231
206
|
|
|
@@ -242,30 +217,9 @@ class SessionManager:
|
|
|
242
217
|
session = WindowsTerminalSession(term_id, pty_proc, channel)
|
|
243
218
|
else:
|
|
244
219
|
# Unix: try real PTY for proper TTY semantics
|
|
245
|
-
pty_success = False
|
|
246
220
|
try:
|
|
247
221
|
import pty
|
|
248
|
-
|
|
249
|
-
logger.debug("Attempting to allocate PTY for terminal %s", term_id)
|
|
250
222
|
master_fd, slave_fd = pty.openpty()
|
|
251
|
-
|
|
252
|
-
# Set terminal attributes for better compatibility
|
|
253
|
-
if _HAS_TERMIOS:
|
|
254
|
-
try:
|
|
255
|
-
# Get current terminal settings
|
|
256
|
-
attrs = termios.tcgetattr(slave_fd)
|
|
257
|
-
# Enable canonical mode and echo
|
|
258
|
-
attrs[3] |= termios.ICANON | termios.ECHO | termios.ECHOE | termios.ECHOK
|
|
259
|
-
# Set input and output modes
|
|
260
|
-
attrs[0] |= termios.ICRNL # Map CR to NL on input
|
|
261
|
-
attrs[1] |= termios.OPOST | termios.ONLCR # Map NL to CR-NL on output
|
|
262
|
-
termios.tcsetattr(slave_fd, termios.TCSANOW, attrs)
|
|
263
|
-
logger.debug("Successfully configured terminal attributes")
|
|
264
|
-
except Exception as e:
|
|
265
|
-
logger.warning("Failed to configure terminal attributes: %s", e)
|
|
266
|
-
else:
|
|
267
|
-
logger.debug("termios not available, skipping terminal attribute configuration")
|
|
268
|
-
|
|
269
223
|
proc = await asyncio.create_subprocess_exec(
|
|
270
224
|
shell,
|
|
271
225
|
stdin=slave_fd,
|
|
@@ -275,10 +229,6 @@ class SessionManager:
|
|
|
275
229
|
cwd=cwd,
|
|
276
230
|
env=_build_child_env(),
|
|
277
231
|
)
|
|
278
|
-
|
|
279
|
-
# Close slave_fd in parent process
|
|
280
|
-
os.close(slave_fd)
|
|
281
|
-
|
|
282
232
|
# Wrap master_fd into a StreamReader
|
|
283
233
|
loop = asyncio.get_running_loop()
|
|
284
234
|
reader = asyncio.StreamReader()
|
|
@@ -290,49 +240,16 @@ class SessionManager:
|
|
|
290
240
|
lambda: asyncio.Protocol(), os.fdopen(master_fd, "wb", buffering=0)
|
|
291
241
|
)
|
|
292
242
|
proc.stdin = asyncio.StreamWriter(writer_transport, writer_protocol, reader, loop)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
logger.info("Successfully allocated PTY for terminal %s", term_id)
|
|
296
|
-
|
|
297
|
-
except Exception as e:
|
|
298
|
-
logger.warning("Failed to allocate PTY for terminal %s: %s", term_id, e)
|
|
299
|
-
|
|
300
|
-
# Enhanced fallback with proper shell invocation
|
|
301
|
-
logger.info("Using enhanced pipe fallback for terminal %s", term_id)
|
|
302
|
-
|
|
303
|
-
# Create enhanced environment for pipe mode
|
|
304
|
-
pipe_env = _build_child_env()
|
|
305
|
-
pipe_env["TERM"] = "dumb" # Use dumb terminal for pipes
|
|
306
|
-
pipe_env["PS1"] = "$ " # Simple prompt for pipes
|
|
307
|
-
|
|
308
|
-
# Use shell with explicit interactive and login flags
|
|
309
|
-
shell_args = [shell]
|
|
310
|
-
if shell.endswith("bash"):
|
|
311
|
-
shell_args.extend(["-i", "-l"]) # Interactive and login shell
|
|
312
|
-
elif shell.endswith("sh"):
|
|
313
|
-
shell_args.extend(["-i"]) # Interactive shell
|
|
314
|
-
|
|
243
|
+
except Exception:
|
|
244
|
+
logger.warning("Failed to allocate PTY, falling back to pipes")
|
|
315
245
|
proc = await asyncio.create_subprocess_exec(
|
|
316
|
-
|
|
246
|
+
shell,
|
|
317
247
|
stdin=asyncio.subprocess.PIPE,
|
|
318
248
|
stdout=asyncio.subprocess.PIPE,
|
|
319
249
|
stderr=asyncio.subprocess.STDOUT,
|
|
320
250
|
cwd=cwd,
|
|
321
|
-
env=
|
|
251
|
+
env=_build_child_env(),
|
|
322
252
|
)
|
|
323
|
-
|
|
324
|
-
# Send initial command to set up better terminal behavior
|
|
325
|
-
if proc.stdin:
|
|
326
|
-
initial_setup = (
|
|
327
|
-
"stty -echo 2>/dev/null || true\n" # Disable echo to prevent double output
|
|
328
|
-
"set +o posix 2>/dev/null || true\n" # Disable POSIX mode if possible
|
|
329
|
-
"PS1='$ '\n" # Set simple prompt
|
|
330
|
-
"export PS1\n"
|
|
331
|
-
"clear 2>/dev/null || echo 'Terminal ready'\n"
|
|
332
|
-
)
|
|
333
|
-
proc.stdin.write(initial_setup.encode())
|
|
334
|
-
await proc.stdin.drain()
|
|
335
|
-
|
|
336
253
|
session = TerminalSession(term_id, proc, channel)
|
|
337
254
|
|
|
338
255
|
self._sessions[term_id] = session
|
|
@@ -344,7 +261,6 @@ class SessionManager:
|
|
|
344
261
|
"pid": session.proc.pid,
|
|
345
262
|
"shell": shell,
|
|
346
263
|
"cwd": cwd,
|
|
347
|
-
"pty_mode": pty_success if not _IS_WINDOWS else True,
|
|
348
264
|
}
|
|
349
265
|
|
|
350
266
|
def get_session(self, terminal_id: str) -> Optional[TerminalSession]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/file_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
{portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/system_handlers.py
RENAMED
|
File without changes
|
{portacode-0.3.3.dev0 → portacode-0.3.4.dev0}/portacode/connection/handlers/terminal_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|