portacode 0.3.12.dev5__tar.gz → 0.3.12.dev7__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.dev7/.claude/agents/communication-manager.md +5 -0
- portacode-0.3.12.dev7/.claude/settings.local.json +9 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/PKG-INFO +1 -1
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/docker-compose.yaml +0 -24
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/_version.py +2 -2
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +112 -3
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/session.py +11 -7
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/terminal.py +66 -3
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode.egg-info/SOURCES.txt +1 -0
- portacode-0.3.12.dev5/.claude/settings.local.json +0 -8
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/.gitignore +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/.gitmodules +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/LICENSE +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/MANIFEST.in +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/Makefile +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/README.md +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/backup.sh +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/README.md +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/__init__.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/__main__.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/cli.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/README.md +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/client.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/data.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/keypair.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/service.py +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/pyproject.toml +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/restore.sh +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/setup.cfg +0 -0
- {portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/setup.py +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: communication-manager
|
|
3
|
+
description: Expert code protocol engineer. Accurately defines and improves simple yet straightforward and concise scallable communication protocols for websocket communication between devices and user sessions
|
|
4
|
+
tools: Read, Grep, Glob
|
|
5
|
+
---
|
|
@@ -32,30 +32,6 @@ services:
|
|
|
32
32
|
volumes:
|
|
33
33
|
- ./server/portacode_django:/app
|
|
34
34
|
|
|
35
|
-
portacode-gateway:
|
|
36
|
-
build:
|
|
37
|
-
context: ./server
|
|
38
|
-
dockerfile: Dockerfile
|
|
39
|
-
container_name: portacode-gateway
|
|
40
|
-
restart: unless-stopped
|
|
41
|
-
env_file:
|
|
42
|
-
- main.env
|
|
43
|
-
depends_on:
|
|
44
|
-
db:
|
|
45
|
-
condition: service_healthy
|
|
46
|
-
redis:
|
|
47
|
-
condition: service_started
|
|
48
|
-
ports:
|
|
49
|
-
- "8000:8000"
|
|
50
|
-
volumes:
|
|
51
|
-
- ./server:/app
|
|
52
|
-
|
|
53
|
-
redis:
|
|
54
|
-
image: redis:7-alpine
|
|
55
|
-
container_name: portacode-redis
|
|
56
|
-
restart: unless-stopped
|
|
57
|
-
ports:
|
|
58
|
-
- "6379:6379"
|
|
59
35
|
|
|
60
36
|
volumes:
|
|
61
37
|
pgdata:
|
|
@@ -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.dev7'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 3, 12, 'dev7')
|
{portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md
RENAMED
|
@@ -2,6 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
This document outlines the WebSocket communication protocol between the Portacode server and the connected client devices.
|
|
4
4
|
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Raw Message Format](#raw-message-format)
|
|
8
|
+
- [Actions](#actions)
|
|
9
|
+
- [Terminal Actions](#terminal-actions)
|
|
10
|
+
- [`terminal_start`](#terminal_start)
|
|
11
|
+
- [`terminal_send`](#terminal_send)
|
|
12
|
+
- [`terminal_stop`](#terminal_stop)
|
|
13
|
+
- [`terminal_list`](#terminal_list)
|
|
14
|
+
- [System Actions](#system-actions)
|
|
15
|
+
- [`system_info`](#system_info)
|
|
16
|
+
- [File Actions](#file-actions)
|
|
17
|
+
- [`file_read`](#file_read)
|
|
18
|
+
- [`file_write`](#file_write)
|
|
19
|
+
- [`directory_list`](#directory_list)
|
|
20
|
+
- [`file_info`](#file_info)
|
|
21
|
+
- [`file_delete`](#file_delete)
|
|
22
|
+
- [Client Session Management](#client-session-management)
|
|
23
|
+
- [`client_sessions_update`](#client_sessions_update)
|
|
24
|
+
- [Events](#events)
|
|
25
|
+
- [Error Events](#error-events)
|
|
26
|
+
- [`error`](#error)
|
|
27
|
+
- [Terminal Events](#terminal-events)
|
|
28
|
+
- [`terminal_started`](#terminal_started)
|
|
29
|
+
- [`terminal_exit`](#terminal_exit)
|
|
30
|
+
- [`terminal_send_ack`](#terminal_send_ack)
|
|
31
|
+
- [`terminal_stopped`](#terminal_stopped)
|
|
32
|
+
- [`terminal_stop_completed`](#terminal_stop_completed)
|
|
33
|
+
- [`terminal_list`](#terminal_list-event)
|
|
34
|
+
- [System Events](#system-events)
|
|
35
|
+
- [`system_info`](#system_info-event)
|
|
36
|
+
- [File Events](#file-events)
|
|
37
|
+
- [`file_read_response`](#file_read_response)
|
|
38
|
+
- [`file_write_response`](#file_write_response)
|
|
39
|
+
- [`directory_list_response`](#directory_list_response)
|
|
40
|
+
- [`file_info_response`](#file_info_response)
|
|
41
|
+
- [`file_delete_response`](#file_delete_response)
|
|
42
|
+
- [Client Session Events](#client-session-events)
|
|
43
|
+
- [`request_client_sessions`](#request_client_sessions)
|
|
44
|
+
- [Terminal Data](#terminal-data)
|
|
45
|
+
- [Terminal I/O Data](#terminal_data)
|
|
46
|
+
- [Server-Side Events](#server-side-events)
|
|
47
|
+
- [`device_status`](#device_status)
|
|
48
|
+
- [`devices`](#devices)
|
|
49
|
+
|
|
5
50
|
## Raw Message Format
|
|
6
51
|
|
|
7
52
|
All communication over the WebSocket is managed by a [multiplexer](./multiplex.py) that wraps every message in a JSON object with a `channel` and a `payload`. This allows for multiple virtual communication channels over a single connection.
|
|
@@ -41,7 +86,7 @@ Actions are messages sent from the server to the device, placed within the `payl
|
|
|
41
86
|
|
|
42
87
|
* `command` (string, mandatory): The name of the action to be executed (e.g., `terminal_start`).
|
|
43
88
|
* `payload` (object, mandatory): An object containing the specific arguments for the action.
|
|
44
|
-
* `reply_channel` (string, optional): A channel name
|
|
89
|
+
* `reply_channel` (string, optional): **DEPRECATED** - A channel name for backward compatibility. Modern implementations should use the `client_sessions` mechanism instead.
|
|
45
90
|
|
|
46
91
|
### `terminal_start`
|
|
47
92
|
|
|
@@ -177,6 +222,20 @@ Deletes a file or directory. Handled by [`file_delete`](./file_handlers.py).
|
|
|
177
222
|
* On success, the device will respond with a [`file_delete_response`](#file_delete_response) event.
|
|
178
223
|
* On error, a generic [`error`](#error) event is sent.
|
|
179
224
|
|
|
225
|
+
### Client Session Management
|
|
226
|
+
|
|
227
|
+
### `client_sessions_update`
|
|
228
|
+
|
|
229
|
+
Sends updated client session information to the device. This is a special internal action used by the server to inform devices about connected client sessions.
|
|
230
|
+
|
|
231
|
+
**Payload Fields:**
|
|
232
|
+
|
|
233
|
+
* `sessions` (array, mandatory): Array of client session objects containing connection information.
|
|
234
|
+
|
|
235
|
+
**Responses:**
|
|
236
|
+
|
|
237
|
+
This action does not generate a response event.
|
|
238
|
+
|
|
180
239
|
---
|
|
181
240
|
|
|
182
241
|
## Events
|
|
@@ -194,7 +253,8 @@ Events are messages sent from the device to the server, placed within the `paylo
|
|
|
194
253
|
```
|
|
195
254
|
|
|
196
255
|
* `event` (string, mandatory): The name of the event being sent (e.g., `terminal_started`).
|
|
197
|
-
* <a name="reply_channel"></a>`reply_channel` (string, optional):
|
|
256
|
+
* <a name="reply_channel"></a>`reply_channel` (string, optional): **DEPRECATED** - For backward compatibility only. Modern events include `client_sessions` array for targeting.
|
|
257
|
+
* `client_sessions` (array, optional): Array of client session channel names that should receive this event. This is the modern way to target specific connected clients.
|
|
198
258
|
|
|
199
259
|
### <a name="error"></a>`error`
|
|
200
260
|
|
|
@@ -328,4 +388,53 @@ Confirms that a file or directory has been deleted in response to a `file_delete
|
|
|
328
388
|
|
|
329
389
|
* `path` (string, mandatory): The path of the deleted file or directory.
|
|
330
390
|
* `deleted_type` (string, mandatory): The type of the deleted item ("file" or "directory").
|
|
331
|
-
* `success` (boolean, mandatory): Indicates whether the deletion was successful.
|
|
391
|
+
* `success` (boolean, mandatory): Indicates whether the deletion was successful.
|
|
392
|
+
|
|
393
|
+
### Client Session Events
|
|
394
|
+
|
|
395
|
+
### <a name="request_client_sessions"></a>`request_client_sessions`
|
|
396
|
+
|
|
397
|
+
Sent by the device to request the current list of connected client sessions from the server. This is an internal event used during device initialization and reconnection.
|
|
398
|
+
|
|
399
|
+
**Event Fields:**
|
|
400
|
+
|
|
401
|
+
This event carries no additional fields.
|
|
402
|
+
|
|
403
|
+
### Terminal Data
|
|
404
|
+
|
|
405
|
+
### <a name="terminal_data"></a>Terminal I/O Data
|
|
406
|
+
|
|
407
|
+
Terminal input/output data is sent directly on terminal channels (not on the control channel). Each terminal session has its own dedicated channel identified by the terminal's UUID.
|
|
408
|
+
|
|
409
|
+
**Terminal Data Format:**
|
|
410
|
+
|
|
411
|
+
```json
|
|
412
|
+
{
|
|
413
|
+
"channel": "<terminal_uuid>",
|
|
414
|
+
"payload": "<terminal_output_string>"
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
* 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 is used for terminal I/O data
|
|
421
|
+
|
|
422
|
+
### Server-Side Events
|
|
423
|
+
|
|
424
|
+
### <a name="device_status"></a>`device_status`
|
|
425
|
+
|
|
426
|
+
Sent by the server to clients to indicate device online/offline status changes.
|
|
427
|
+
|
|
428
|
+
**Event Fields:**
|
|
429
|
+
|
|
430
|
+
* `device` (object, mandatory): Device status information
|
|
431
|
+
* `id` (integer, mandatory): Device ID
|
|
432
|
+
* `online` (boolean, mandatory): Whether the device is online
|
|
433
|
+
|
|
434
|
+
### <a name="devices"></a>`devices`
|
|
435
|
+
|
|
436
|
+
Sent by the server to clients to provide initial device list snapshot.
|
|
437
|
+
|
|
438
|
+
**Event Fields:**
|
|
439
|
+
|
|
440
|
+
* `devices` (array, mandatory): Array of device objects with status information
|
|
@@ -78,8 +78,15 @@ class TerminalSession:
|
|
|
78
78
|
logger.warning("stdin pipe closed for terminal %s", self.id)
|
|
79
79
|
return
|
|
80
80
|
try:
|
|
81
|
-
self.proc.stdin
|
|
82
|
-
|
|
81
|
+
if hasattr(self.proc.stdin, 'write') and hasattr(self.proc.stdin, 'drain'):
|
|
82
|
+
# StreamWriter (pipe fallback)
|
|
83
|
+
self.proc.stdin.write(data.encode())
|
|
84
|
+
await self.proc.stdin.drain()
|
|
85
|
+
else:
|
|
86
|
+
# File object (PTY)
|
|
87
|
+
loop = asyncio.get_running_loop()
|
|
88
|
+
await loop.run_in_executor(None, self.proc.stdin.write, data.encode())
|
|
89
|
+
await loop.run_in_executor(None, self.proc.stdin.flush)
|
|
83
90
|
except Exception as exc:
|
|
84
91
|
logger.warning("Failed to write to terminal %s: %s", self.id, exc)
|
|
85
92
|
|
|
@@ -301,11 +308,8 @@ class SessionManager:
|
|
|
301
308
|
protocol = asyncio.StreamReaderProtocol(reader)
|
|
302
309
|
await loop.connect_read_pipe(lambda: protocol, os.fdopen(master_fd, "rb", buffering=0))
|
|
303
310
|
proc.stdout = reader
|
|
304
|
-
# Use writer for stdin
|
|
305
|
-
|
|
306
|
-
lambda: asyncio.Protocol(), os.fdopen(master_fd, "wb", buffering=0)
|
|
307
|
-
)
|
|
308
|
-
proc.stdin = asyncio.StreamWriter(writer_transport, writer_protocol, reader, loop)
|
|
311
|
+
# Use writer for stdin - create a simple file-like wrapper
|
|
312
|
+
proc.stdin = os.fdopen(master_fd, "wb", buffering=0)
|
|
309
313
|
except Exception:
|
|
310
314
|
logger.warning("Failed to allocate PTY, falling back to pipes")
|
|
311
315
|
proc = await asyncio.create_subprocess_exec(
|
|
@@ -92,7 +92,12 @@ class ClientSessionManager:
|
|
|
92
92
|
|
|
93
93
|
target_sessions = []
|
|
94
94
|
for session in self._client_sessions.values():
|
|
95
|
-
#
|
|
95
|
+
# Dashboard sessions should receive ALL events regardless of project_id
|
|
96
|
+
if session.get("connection_type") == "dashboard":
|
|
97
|
+
target_sessions.append(session.get("channel_name"))
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
# For project sessions, filter by project_id if specified
|
|
96
101
|
if project_id and session.get("project_id") != project_id:
|
|
97
102
|
continue
|
|
98
103
|
target_sessions.append(session.get("channel_name"))
|
|
@@ -241,9 +246,17 @@ class TerminalManager:
|
|
|
241
246
|
# Handle client sessions update directly (special case)
|
|
242
247
|
if cmd == "client_sessions_update":
|
|
243
248
|
sessions = message.get("sessions", [])
|
|
244
|
-
logger.info("terminal_manager:
|
|
249
|
+
logger.info("terminal_manager: 🔔 RECEIVED client_sessions_update with %d sessions", len(sessions))
|
|
250
|
+
logger.debug("terminal_manager: Session details: %s", sessions)
|
|
245
251
|
self._client_session_manager.update_sessions(sessions)
|
|
246
|
-
logger.info("terminal_manager: Updated client sessions (%d sessions)", len(sessions))
|
|
252
|
+
logger.info("terminal_manager: ✅ Updated client sessions (%d sessions)", len(sessions))
|
|
253
|
+
|
|
254
|
+
# Auto-send initial data to new clients
|
|
255
|
+
if len(sessions) > 0:
|
|
256
|
+
logger.info("terminal_manager: 🚀 Triggering auto-send of initial data to clients")
|
|
257
|
+
await self._send_initial_data_to_clients()
|
|
258
|
+
else:
|
|
259
|
+
logger.info("terminal_manager: ℹ️ No sessions to send data to")
|
|
247
260
|
continue
|
|
248
261
|
|
|
249
262
|
# Dispatch command through registry
|
|
@@ -257,6 +270,56 @@ class TerminalManager:
|
|
|
257
270
|
# Continue processing other messages
|
|
258
271
|
continue
|
|
259
272
|
|
|
273
|
+
async def _send_initial_data_to_clients(self):
|
|
274
|
+
"""Send initial system info and terminal list to connected clients."""
|
|
275
|
+
logger.info("terminal_manager: 📤 Starting to send initial data to connected clients")
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
# Send system_info
|
|
279
|
+
logger.info("terminal_manager: 📊 Dispatching system_info command")
|
|
280
|
+
await self._command_registry.dispatch("system_info", {}, None)
|
|
281
|
+
logger.info("terminal_manager: ✅ system_info dispatch completed")
|
|
282
|
+
|
|
283
|
+
# Send terminal_list for each project that has connected clients
|
|
284
|
+
logger.info("terminal_manager: 📋 Preparing to send terminal_list to clients")
|
|
285
|
+
|
|
286
|
+
# Get unique project IDs from connected clients
|
|
287
|
+
project_ids = set()
|
|
288
|
+
all_sessions = self._client_session_manager.get_sessions()
|
|
289
|
+
logger.info(f"terminal_manager: Analyzing {len(all_sessions)} client sessions for project IDs")
|
|
290
|
+
|
|
291
|
+
for session in all_sessions.values():
|
|
292
|
+
project_id = session.get("project_id")
|
|
293
|
+
connection_type = session.get("connection_type", "unknown")
|
|
294
|
+
logger.debug(f"terminal_manager: Session {session.get('channel_name')}: project_id={project_id}, type={connection_type}")
|
|
295
|
+
if project_id:
|
|
296
|
+
project_ids.add(project_id)
|
|
297
|
+
|
|
298
|
+
logger.info(f"terminal_manager: Found {len(project_ids)} unique project IDs: {list(project_ids)}")
|
|
299
|
+
|
|
300
|
+
# Send terminal_list for each project, plus one without project_id for general sessions
|
|
301
|
+
if not project_ids:
|
|
302
|
+
# No specific projects, send general terminal_list
|
|
303
|
+
logger.info("terminal_manager: 📋 Dispatching general terminal_list (no specific projects)")
|
|
304
|
+
await self._command_registry.dispatch("terminal_list", {}, None)
|
|
305
|
+
logger.info("terminal_manager: ✅ General terminal_list dispatch completed")
|
|
306
|
+
else:
|
|
307
|
+
# Send terminal_list for each project
|
|
308
|
+
for project_id in project_ids:
|
|
309
|
+
logger.info(f"terminal_manager: 📋 Dispatching terminal_list for project {project_id}")
|
|
310
|
+
await self._command_registry.dispatch("terminal_list", {"project_id": project_id}, None)
|
|
311
|
+
logger.info(f"terminal_manager: ✅ Project {project_id} terminal_list dispatch completed")
|
|
312
|
+
|
|
313
|
+
# Also send general terminal_list for dashboard connections
|
|
314
|
+
logger.info("terminal_manager: 📋 Dispatching general terminal_list for dashboard connections")
|
|
315
|
+
await self._command_registry.dispatch("terminal_list", {}, None)
|
|
316
|
+
logger.info("terminal_manager: ✅ General terminal_list for dashboard dispatch completed")
|
|
317
|
+
|
|
318
|
+
logger.info("terminal_manager: 🎉 All initial data sent successfully")
|
|
319
|
+
|
|
320
|
+
except Exception as exc:
|
|
321
|
+
logger.exception("terminal_manager: ❌ Error sending initial data to clients: %s", exc)
|
|
322
|
+
|
|
260
323
|
# ------------------------------------------------------------------
|
|
261
324
|
# Extension API
|
|
262
325
|
# ------------------------------------------------------------------
|
|
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.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/file_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
{portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/portacode/connection/handlers/system_handlers.py
RENAMED
|
File without changes
|
{portacode-0.3.12.dev5 → portacode-0.3.12.dev7}/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
|