portacode 0.3.12.dev6__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.dev6 → portacode-0.3.12.dev7}/PKG-INFO +1 -1
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/docker-compose.yaml +0 -24
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/_version.py +2 -2
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +112 -3
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/session.py +11 -7
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/terminal.py +33 -8
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode.egg-info/SOURCES.txt +1 -0
- portacode-0.3.12.dev6/.claude/settings.local.json +0 -8
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/.gitignore +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/.gitmodules +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/LICENSE +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/MANIFEST.in +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/Makefile +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/README.md +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/backup.sh +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/README.md +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/__init__.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/__main__.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/cli.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/README.md +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/client.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/data.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/keypair.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/service.py +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/pyproject.toml +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/restore.sh +0 -0
- {portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/setup.cfg +0 -0
- {portacode-0.3.12.dev6 → 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.dev6 → 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,13 +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))
|
|
247
253
|
|
|
248
254
|
# Auto-send initial data to new clients
|
|
249
255
|
if len(sessions) > 0:
|
|
256
|
+
logger.info("terminal_manager: 🚀 Triggering auto-send of initial data to clients")
|
|
250
257
|
await self._send_initial_data_to_clients()
|
|
258
|
+
else:
|
|
259
|
+
logger.info("terminal_manager: ℹ️ No sessions to send data to")
|
|
251
260
|
continue
|
|
252
261
|
|
|
253
262
|
# Dispatch command through registry
|
|
@@ -263,37 +272,53 @@ class TerminalManager:
|
|
|
263
272
|
|
|
264
273
|
async def _send_initial_data_to_clients(self):
|
|
265
274
|
"""Send initial system info and terminal list to connected clients."""
|
|
266
|
-
logger.info("terminal_manager:
|
|
275
|
+
logger.info("terminal_manager: 📤 Starting to send initial data to connected clients")
|
|
267
276
|
|
|
268
277
|
try:
|
|
269
278
|
# Send system_info
|
|
270
|
-
logger.info("terminal_manager:
|
|
279
|
+
logger.info("terminal_manager: 📊 Dispatching system_info command")
|
|
271
280
|
await self._command_registry.dispatch("system_info", {}, None)
|
|
281
|
+
logger.info("terminal_manager: ✅ system_info dispatch completed")
|
|
272
282
|
|
|
273
283
|
# Send terminal_list for each project that has connected clients
|
|
274
|
-
logger.info("terminal_manager:
|
|
284
|
+
logger.info("terminal_manager: 📋 Preparing to send terminal_list to clients")
|
|
275
285
|
|
|
276
286
|
# Get unique project IDs from connected clients
|
|
277
287
|
project_ids = set()
|
|
278
|
-
|
|
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():
|
|
279
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}")
|
|
280
295
|
if project_id:
|
|
281
296
|
project_ids.add(project_id)
|
|
282
297
|
|
|
298
|
+
logger.info(f"terminal_manager: Found {len(project_ids)} unique project IDs: {list(project_ids)}")
|
|
299
|
+
|
|
283
300
|
# Send terminal_list for each project, plus one without project_id for general sessions
|
|
284
301
|
if not project_ids:
|
|
285
302
|
# No specific projects, send general terminal_list
|
|
303
|
+
logger.info("terminal_manager: 📋 Dispatching general terminal_list (no specific projects)")
|
|
286
304
|
await self._command_registry.dispatch("terminal_list", {}, None)
|
|
305
|
+
logger.info("terminal_manager: ✅ General terminal_list dispatch completed")
|
|
287
306
|
else:
|
|
288
307
|
# Send terminal_list for each project
|
|
289
308
|
for project_id in project_ids:
|
|
309
|
+
logger.info(f"terminal_manager: 📋 Dispatching terminal_list for project {project_id}")
|
|
290
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")
|
|
291
312
|
|
|
292
313
|
# Also send general terminal_list for dashboard connections
|
|
314
|
+
logger.info("terminal_manager: 📋 Dispatching general terminal_list for dashboard connections")
|
|
293
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")
|
|
294
319
|
|
|
295
320
|
except Exception as exc:
|
|
296
|
-
logger.exception("terminal_manager: Error sending initial data to clients: %s", exc)
|
|
321
|
+
logger.exception("terminal_manager: ❌ Error sending initial data to clients: %s", exc)
|
|
297
322
|
|
|
298
323
|
# ------------------------------------------------------------------
|
|
299
324
|
# Extension API
|
|
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.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/file_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
{portacode-0.3.12.dev6 → portacode-0.3.12.dev7}/portacode/connection/handlers/system_handlers.py
RENAMED
|
File without changes
|
{portacode-0.3.12.dev6 → 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
|