portacode 0.3.12.dev8__tar.gz → 0.3.12.dev9__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.12.dev8 → portacode-0.3.12.dev9}/PKG-INFO +1 -1
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/_version.py +2 -2
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +79 -11
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/session.py +108 -34
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/.claude/agents/communication-manager.md +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/.claude/settings.local.json +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/.gitignore +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/.gitmodules +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/LICENSE +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/MANIFEST.in +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/Makefile +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/README.md +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/backup.sh +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/docker-compose.yaml +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/README.md +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/__init__.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/__main__.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/cli.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/README.md +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/client.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/terminal.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/data.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/keypair.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/service.py +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode.egg-info/SOURCES.txt +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/pyproject.toml +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/restore.sh +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/setup.cfg +0 -0
- {portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/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.12.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 3, 12, '
|
|
20
|
+
__version__ = version = '0.3.12.dev9'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 3, 12, 'dev9')
|
{portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md
RENAMED
|
@@ -26,6 +26,7 @@ This document outlines the WebSocket communication protocol between the Portacod
|
|
|
26
26
|
- [`error`](#error)
|
|
27
27
|
- [Terminal Events](#terminal-events)
|
|
28
28
|
- [`terminal_started`](#terminal_started)
|
|
29
|
+
- [`terminal_data`](#terminal_data)
|
|
29
30
|
- [`terminal_exit`](#terminal_exit)
|
|
30
31
|
- [`terminal_send_ack`](#terminal_send_ack)
|
|
31
32
|
- [`terminal_stopped`](#terminal_stopped)
|
|
@@ -79,14 +80,14 @@ Actions are messages sent from the server to the device, placed within the `payl
|
|
|
79
80
|
"payload": {
|
|
80
81
|
"arg1": "value1",
|
|
81
82
|
"...": "..."
|
|
82
|
-
}
|
|
83
|
-
"reply_channel": "<channel_name>"
|
|
83
|
+
}
|
|
84
84
|
}
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
* `command` (string, mandatory): The name of the action to be executed (e.g., `terminal_start`).
|
|
88
88
|
* `payload` (object, mandatory): An object containing the specific arguments for the action.
|
|
89
|
-
|
|
89
|
+
|
|
90
|
+
**Note**: Actions do not require targeting information - responses are automatically routed using the client session management system.
|
|
90
91
|
|
|
91
92
|
### `terminal_start`
|
|
92
93
|
|
|
@@ -248,13 +249,22 @@ Events are messages sent from the device to the server, placed within the `paylo
|
|
|
248
249
|
{
|
|
249
250
|
"event": "<event_name>",
|
|
250
251
|
// Event-specific fields...
|
|
251
|
-
"
|
|
252
|
+
"device_id": 123,
|
|
253
|
+
"project_id": "<project_uuid>",
|
|
254
|
+
"client_sessions": ["channel.abc123", "channel.def456"]
|
|
252
255
|
}
|
|
253
256
|
```
|
|
254
257
|
|
|
258
|
+
**Standard Fields (automatically added by the system):**
|
|
259
|
+
|
|
255
260
|
* `event` (string, mandatory): The name of the event being sent (e.g., `terminal_started`).
|
|
256
|
-
*
|
|
257
|
-
* `
|
|
261
|
+
* `device_id` (integer, mandatory): The ID of the authenticated device that generated this event. **Added by the server based on the authenticated connection for security** - devices cannot self-identify.
|
|
262
|
+
* `project_id` (string, optional): The project UUID associated with this event, used for project-scoped filtering. Sent by the device.
|
|
263
|
+
* `client_sessions` (array, optional): Array of client session channel names that should receive this event. **Added by the device's terminal manager** based on interested client sessions. When present, the event is sent only to these specific sessions. When absent, the event is broadcast to all sessions for the device owner.
|
|
264
|
+
|
|
265
|
+
**Event-Specific Fields:**
|
|
266
|
+
|
|
267
|
+
Each event type includes additional fields specific to its purpose, documented in the individual event sections below.
|
|
258
268
|
|
|
259
269
|
### <a name="error"></a>`error`
|
|
260
270
|
|
|
@@ -274,6 +284,41 @@ Confirms that a new terminal session has been successfully started. Triggered by
|
|
|
274
284
|
* `channel` (string, mandatory): The channel name for terminal I/O.
|
|
275
285
|
* `project_id` (string, optional): The project ID associated with the terminal.
|
|
276
286
|
|
|
287
|
+
### <a name="terminal_data"></a>`terminal_data`
|
|
288
|
+
|
|
289
|
+
Streams real-time terminal output data from a running terminal session to connected clients. This event is automatically generated whenever the terminal process produces output (stdout/stderr). Generated by [`TerminalSession`](./session.py) through the terminal manager's session-aware messaging system.
|
|
290
|
+
|
|
291
|
+
**Event-Specific Fields:**
|
|
292
|
+
|
|
293
|
+
* `channel` (string, mandatory): The terminal UUID identifying which terminal session produced this output. This matches the `terminal_id` from the corresponding `terminal_started` event.
|
|
294
|
+
* `data` (string, mandatory): The raw terminal output data. **See detailed description below.**
|
|
295
|
+
|
|
296
|
+
**The `data` Field - Detailed Specification:**
|
|
297
|
+
|
|
298
|
+
The `data` field contains the exact bytes output by the terminal process, decoded as a UTF-8 string with error handling:
|
|
299
|
+
|
|
300
|
+
* **Encoding**: UTF-8 with `errors="ignore"` - invalid UTF-8 sequences are silently dropped
|
|
301
|
+
* **Content**: Raw terminal output including:
|
|
302
|
+
- Regular command output (stdout)
|
|
303
|
+
- Error messages (stderr) - combined with stdout in PTY mode
|
|
304
|
+
- ANSI escape sequences for colors, cursor positioning, screen clearing, etc.
|
|
305
|
+
- Control characters (newlines, tabs, backspace, etc.)
|
|
306
|
+
- Shell prompts and interactive application output
|
|
307
|
+
* **Buffering**: Data is read in 1024-byte chunks from the terminal process and sent immediately (no line buffering)
|
|
308
|
+
* **Binary Safety**: Binary data is handled via UTF-8 decoding with error tolerance
|
|
309
|
+
* **Size**: Individual chunks are typically ≤1024 characters, but can be smaller for real-time responsiveness
|
|
310
|
+
|
|
311
|
+
**Examples of `data` content:**
|
|
312
|
+
```
|
|
313
|
+
"Hello, World!\n" // Simple command output
|
|
314
|
+
"\u001b[32mSuccess\u001b[0m\n" // ANSI colored text
|
|
315
|
+
"user@host:~/project$ " // Shell prompt
|
|
316
|
+
"\u001b[2J\u001b[H" // Clear screen escape sequence
|
|
317
|
+
"Progress: [████████████████████] 100%\r" // Progress bar with carriage return
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Security Note**: The `device_id` field is automatically injected by the server based on the authenticated connection - the device cannot and should not specify its own ID. The `project_id` and `client_sessions` fields are added by the device's terminal manager for proper routing and filtering.
|
|
321
|
+
|
|
277
322
|
### <a name="terminal_exit"></a>`terminal_exit`
|
|
278
323
|
|
|
279
324
|
Notifies the server that a terminal session has terminated. This can be due to the process ending or the session being stopped. Handled by [`terminal_start`](./terminal_handlers.py).
|
|
@@ -402,11 +447,33 @@ This event carries no additional fields.
|
|
|
402
447
|
|
|
403
448
|
### Terminal Data
|
|
404
449
|
|
|
405
|
-
###
|
|
450
|
+
### Terminal I/O Data Formats
|
|
451
|
+
|
|
452
|
+
Terminal I/O data can be sent in two formats depending on the implementation:
|
|
453
|
+
|
|
454
|
+
#### Modern Format (Recommended)
|
|
455
|
+
|
|
456
|
+
Terminal data is sent as a proper [`terminal_data`](#terminal_data) event on the control channel (channel 0) with client session targeting support:
|
|
457
|
+
|
|
458
|
+
```json
|
|
459
|
+
{
|
|
460
|
+
"channel": 0,
|
|
461
|
+
"payload": {
|
|
462
|
+
"event": "terminal_data",
|
|
463
|
+
"channel": "<terminal_uuid>",
|
|
464
|
+
"data": "<terminal_output_string>",
|
|
465
|
+
"device_id": 123,
|
|
466
|
+
"project_id": "<project_uuid>",
|
|
467
|
+
"client_sessions": ["channel.abc123", "channel.def456"]
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
This format follows the standard event structure with automatic system field injection (device_id, project_id, client_sessions) for proper routing and security.
|
|
406
473
|
|
|
407
|
-
|
|
474
|
+
#### Legacy Format (Deprecated)
|
|
408
475
|
|
|
409
|
-
|
|
476
|
+
Terminal data sent directly on terminal channels (not on the control channel):
|
|
410
477
|
|
|
411
478
|
```json
|
|
412
479
|
{
|
|
@@ -416,8 +483,9 @@ Terminal input/output data is sent directly on terminal channels (not on the con
|
|
|
416
483
|
```
|
|
417
484
|
|
|
418
485
|
* Terminal output is sent as raw string data in the payload
|
|
419
|
-
* Input to terminals is sent the same way but in the opposite direction
|
|
420
|
-
* No event wrapper
|
|
486
|
+
* Input to terminals is sent the same way but in the opposite direction
|
|
487
|
+
* No event wrapper or client targeting is used
|
|
488
|
+
* This format broadcasts to all sessions for the device owner
|
|
421
489
|
|
|
422
490
|
### Server-Side Events
|
|
423
491
|
|
|
@@ -4,6 +4,7 @@ import asyncio
|
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
6
|
import sys
|
|
7
|
+
import time
|
|
7
8
|
import uuid
|
|
8
9
|
from asyncio.subprocess import Process
|
|
9
10
|
from pathlib import Path
|
|
@@ -13,6 +14,10 @@ from collections import deque
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
14
15
|
from ..multiplex import Channel
|
|
15
16
|
|
|
17
|
+
# Terminal data rate limiting configuration
|
|
18
|
+
TERMINAL_DATA_RATE_LIMIT_MS = 200 # Minimum time between terminal_data events (milliseconds)
|
|
19
|
+
TERMINAL_DATA_MAX_WAIT_MS = 1000 # Maximum time to wait before sending accumulated data (milliseconds)
|
|
20
|
+
|
|
16
21
|
logger = logging.getLogger(__name__)
|
|
17
22
|
|
|
18
23
|
_IS_WINDOWS = sys.platform.startswith("win")
|
|
@@ -44,6 +49,11 @@ class TerminalSession:
|
|
|
44
49
|
self.terminal_manager = terminal_manager
|
|
45
50
|
self._reader_task: Optional[asyncio.Task[None]] = None
|
|
46
51
|
self._buffer: deque[str] = deque(maxlen=400)
|
|
52
|
+
|
|
53
|
+
# Rate limiting for terminal_data events
|
|
54
|
+
self._last_send_time: float = 0
|
|
55
|
+
self._pending_data: str = ""
|
|
56
|
+
self._debounce_task: Optional[asyncio.Task[None]] = None
|
|
47
57
|
|
|
48
58
|
async def start_io_forwarding(self) -> None:
|
|
49
59
|
"""Spawn background task that copies stdout/stderr to the channel."""
|
|
@@ -57,23 +67,9 @@ class TerminalSession:
|
|
|
57
67
|
break
|
|
58
68
|
text = data.decode(errors="ignore")
|
|
59
69
|
logging.getLogger("portacode.terminal").debug(f"[MUX] Terminal {self.id} output: {text!r}")
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if self.terminal_manager:
|
|
64
|
-
await self.terminal_manager._send_session_aware({
|
|
65
|
-
"event": "terminal_data",
|
|
66
|
-
"channel": self.id,
|
|
67
|
-
"data": text,
|
|
68
|
-
"project_id": self.project_id
|
|
69
|
-
}, project_id=self.project_id)
|
|
70
|
-
else:
|
|
71
|
-
# Fallback to raw channel for backward compatibility
|
|
72
|
-
await self.channel.send(text)
|
|
73
|
-
except Exception as exc:
|
|
74
|
-
logger.warning("Failed to forward terminal output: %s", exc)
|
|
75
|
-
await asyncio.sleep(0.5)
|
|
76
|
-
continue
|
|
70
|
+
|
|
71
|
+
# Use rate-limited sending instead of immediate sending
|
|
72
|
+
await self._handle_terminal_data(text)
|
|
77
73
|
finally:
|
|
78
74
|
if self.proc and self.proc.returncode is None:
|
|
79
75
|
pass # Keep alive across reconnects
|
|
@@ -129,6 +125,20 @@ class TerminalSession:
|
|
|
129
125
|
except asyncio.CancelledError:
|
|
130
126
|
pass
|
|
131
127
|
|
|
128
|
+
# Cancel and flush any pending terminal data
|
|
129
|
+
if self._debounce_task and not self._debounce_task.done():
|
|
130
|
+
logger.info("session.stop: Cancelling debounce task for session %s", self.id)
|
|
131
|
+
self._debounce_task.cancel()
|
|
132
|
+
try:
|
|
133
|
+
await self._debounce_task
|
|
134
|
+
except asyncio.CancelledError:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
# Send any remaining pending data
|
|
138
|
+
if self._pending_data:
|
|
139
|
+
logger.info("session.stop: Flushing pending terminal data for session %s", self.id)
|
|
140
|
+
await self._flush_pending_data()
|
|
141
|
+
|
|
132
142
|
# Wait for process to exit
|
|
133
143
|
if self.proc.returncode is None:
|
|
134
144
|
logger.info("session.stop: Waiting for process to exit for session %s", self.id)
|
|
@@ -143,6 +153,70 @@ class TerminalSession:
|
|
|
143
153
|
logger.exception("session.stop: Error stopping session %s: %s", self.id, exc)
|
|
144
154
|
raise
|
|
145
155
|
|
|
156
|
+
async def _send_terminal_data_now(self, data: str) -> None:
|
|
157
|
+
"""Send terminal data immediately and update last send time."""
|
|
158
|
+
self._last_send_time = time.time()
|
|
159
|
+
|
|
160
|
+
# Add to buffer for snapshots
|
|
161
|
+
self._buffer.append(data)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
# Send terminal data via control channel with client session targeting
|
|
165
|
+
if self.terminal_manager:
|
|
166
|
+
await self.terminal_manager._send_session_aware({
|
|
167
|
+
"event": "terminal_data",
|
|
168
|
+
"channel": self.id,
|
|
169
|
+
"data": data,
|
|
170
|
+
"project_id": self.project_id
|
|
171
|
+
}, project_id=self.project_id)
|
|
172
|
+
else:
|
|
173
|
+
# Fallback to raw channel for backward compatibility
|
|
174
|
+
await self.channel.send(data)
|
|
175
|
+
except Exception as exc:
|
|
176
|
+
logger.warning("Failed to forward terminal output: %s", exc)
|
|
177
|
+
|
|
178
|
+
async def _flush_pending_data(self) -> None:
|
|
179
|
+
"""Send accumulated pending data and reset pending buffer."""
|
|
180
|
+
if self._pending_data:
|
|
181
|
+
data_to_send = self._pending_data
|
|
182
|
+
self._pending_data = ""
|
|
183
|
+
await self._send_terminal_data_now(data_to_send)
|
|
184
|
+
|
|
185
|
+
# Clear the debounce task
|
|
186
|
+
self._debounce_task = None
|
|
187
|
+
|
|
188
|
+
async def _handle_terminal_data(self, data: str) -> None:
|
|
189
|
+
"""Handle new terminal data with rate limiting and debouncing."""
|
|
190
|
+
current_time = time.time()
|
|
191
|
+
time_since_last_send = (current_time - self._last_send_time) * 1000 # Convert to milliseconds
|
|
192
|
+
|
|
193
|
+
# Add new data to pending buffer
|
|
194
|
+
self._pending_data += data
|
|
195
|
+
|
|
196
|
+
# Cancel existing debounce task if any
|
|
197
|
+
if self._debounce_task and not self._debounce_task.done():
|
|
198
|
+
self._debounce_task.cancel()
|
|
199
|
+
|
|
200
|
+
if time_since_last_send >= TERMINAL_DATA_RATE_LIMIT_MS:
|
|
201
|
+
# Enough time has passed, send immediately
|
|
202
|
+
await self._flush_pending_data()
|
|
203
|
+
else:
|
|
204
|
+
# Too soon, set up debounce timer
|
|
205
|
+
async def _debounce_timer():
|
|
206
|
+
try:
|
|
207
|
+
# Wait for either the rate limit period or max wait time
|
|
208
|
+
wait_time = min(
|
|
209
|
+
(TERMINAL_DATA_RATE_LIMIT_MS - time_since_last_send) / 1000,
|
|
210
|
+
TERMINAL_DATA_MAX_WAIT_MS / 1000
|
|
211
|
+
)
|
|
212
|
+
await asyncio.sleep(wait_time)
|
|
213
|
+
await self._flush_pending_data()
|
|
214
|
+
except asyncio.CancelledError:
|
|
215
|
+
# Timer was cancelled, another data event came in
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
self._debounce_task = asyncio.create_task(_debounce_timer())
|
|
219
|
+
|
|
146
220
|
def snapshot_buffer(self) -> str:
|
|
147
221
|
"""Return concatenated last buffer contents suitable for UI."""
|
|
148
222
|
return "".join(self._buffer)
|
|
@@ -197,23 +271,9 @@ class WindowsTerminalSession(TerminalSession):
|
|
|
197
271
|
else:
|
|
198
272
|
text = data
|
|
199
273
|
logging.getLogger("portacode.terminal").debug(f"[MUX] Terminal {self.id} output: {text!r}")
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if self.terminal_manager:
|
|
204
|
-
await self.terminal_manager._send_session_aware({
|
|
205
|
-
"event": "terminal_data",
|
|
206
|
-
"channel": self.id,
|
|
207
|
-
"data": text,
|
|
208
|
-
"project_id": self.project_id
|
|
209
|
-
}, project_id=self.project_id)
|
|
210
|
-
else:
|
|
211
|
-
# Fallback to raw channel for backward compatibility
|
|
212
|
-
await self.channel.send(text)
|
|
213
|
-
except Exception as exc:
|
|
214
|
-
logger.warning("Failed to forward terminal output: %s", exc)
|
|
215
|
-
await asyncio.sleep(0.5)
|
|
216
|
-
continue
|
|
274
|
+
|
|
275
|
+
# Use rate-limited sending instead of immediate sending
|
|
276
|
+
await self._handle_terminal_data(text)
|
|
217
277
|
finally:
|
|
218
278
|
if self._pty and self._pty.isalive():
|
|
219
279
|
self._pty.kill()
|
|
@@ -258,6 +318,20 @@ class WindowsTerminalSession(TerminalSession):
|
|
|
258
318
|
except asyncio.CancelledError:
|
|
259
319
|
pass
|
|
260
320
|
|
|
321
|
+
# Cancel and flush any pending terminal data
|
|
322
|
+
if self._debounce_task and not self._debounce_task.done():
|
|
323
|
+
logger.info("session.stop: Cancelling debounce task for Windows session %s", self.id)
|
|
324
|
+
self._debounce_task.cancel()
|
|
325
|
+
try:
|
|
326
|
+
await self._debounce_task
|
|
327
|
+
except asyncio.CancelledError:
|
|
328
|
+
pass
|
|
329
|
+
|
|
330
|
+
# Send any remaining pending data
|
|
331
|
+
if self._pending_data:
|
|
332
|
+
logger.info("session.stop: Flushing pending terminal data for Windows session %s", self.id)
|
|
333
|
+
await self._flush_pending_data()
|
|
334
|
+
|
|
261
335
|
logger.info("session.stop: Successfully stopped Windows session %s", self.id)
|
|
262
336
|
|
|
263
337
|
except Exception as exc:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/file_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
{portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/portacode/connection/handlers/system_handlers.py
RENAMED
|
File without changes
|
{portacode-0.3.12.dev8 → portacode-0.3.12.dev9}/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
|
|
File without changes
|