git-juggler 0.1.3__tar.gz → 0.1.4__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.
- {git_juggler-0.1.3 → git_juggler-0.1.4}/PKG-INFO +2 -1
- {git_juggler-0.1.3 → git_juggler-0.1.4}/pyproject.toml +1 -0
- git_juggler-0.1.4/src/backend/git_juggler/terminal.py +166 -0
- git_juggler-0.1.3/src/backend/git_juggler/terminal.py +0 -89
- {git_juggler-0.1.3 → git_juggler-0.1.4}/.github/workflows/build-package.yml +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/.gitignore +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/CLAUDE.md +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/LICENSE +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/README.md +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/notes.txt +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/plans/github-actions-settings-and-build-status.md +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/plans/jenkins-settings-and-build-status.md +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/scripts/build-package.sh +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/__init__.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/__init__.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/activity_tracker.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/agent_activity_adapter.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/agent_process_discovery.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/agent_repository_tracker.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/create_process_inspector.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/git_resolver.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/linux_process_inspector.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/mac_process_inspector.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/paths.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/process_info.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/process_inspector.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/worktree_evidence.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/app.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/browse.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/ci.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/commit_detail.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/config.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/dev_app.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/frontend_dist/assets/index-DrLGhTYA.css +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/frontend_dist/assets/index-DxTmj1Il.js +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/frontend_dist/favicon.svg +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/frontend_dist/icons.svg +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/frontend_dist/index.html +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/git_data.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/git_utils.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/github_actions.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/jenkins.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/main.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/repos.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/schemas.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/tests/test_agent_tracking.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/tests/test_git_data.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/tests/test_github_actions.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/tests/test_jenkins.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/tests/test_repos.py +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/.gitignore +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/README.md +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/index.html +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/package-lock.json +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/package.json +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/public/favicon.svg +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/public/icons.svg +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/App.css +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/App.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/api/client.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/api/types.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Agents/AgentActivityPanel.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Badges/BranchBadge.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Badges/CiRunBadge.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Badges/StashBadge.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Badges/TagBadge.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Badges/icons.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Branches/DeleteBranchModal.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Commits/CommitDetail.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Commits/CommitList.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Commits/CommitRow.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Commits/UncommittedRow.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/BranchContextMenu.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/CommitContextMenu.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Graph/GraphPanel.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Graph/branchColor.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Graph/computeColumns.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Menu/MainMenu.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Search/MultiSelect.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Search/SearchBox.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Sidebar/RepoList.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Tabs/TabsBar.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Tags/DeleteTagModal.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Terminal/TerminalPanel.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/index.css +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/index.tsx +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/lib/dismissOnOutsideClick.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/lib/flip.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/lib/formatDate.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/lib/scrollToCommit.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/lib/stripAnsi.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/lib/terminalTheme.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/state/store.ts +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/tsconfig.app.json +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/tsconfig.json +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/tsconfig.node.json +0 -0
- {git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/vite.config.ts +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: git-juggler
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Browse multiple local git repos' commit graphs and a shell, from one local web UI.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.10
|
|
8
8
|
Requires-Dist: fastapi>=0.110
|
|
9
9
|
Requires-Dist: gitpython>=3.1
|
|
10
|
+
Requires-Dist: pywinpty>=2.0; sys_platform == 'win32'
|
|
10
11
|
Requires-Dist: uvicorn[standard]>=0.29
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import threading
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from fastapi import WebSocket, WebSocketDisconnect
|
|
11
|
+
|
|
12
|
+
# Wire protocol (JSON text frames both ways), shared by both platform
|
|
13
|
+
# implementations below:
|
|
14
|
+
# client -> server: {"type": "input", "data": "<keystrokes>"}
|
|
15
|
+
# {"type": "resize", "cols": n, "rows": n}
|
|
16
|
+
# server -> client: {"type": "output", "data": "<shell output>"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def run_terminal_session(websocket: WebSocket, cwd: Path) -> None:
|
|
20
|
+
"""Spawn a real shell in a pseudo-terminal rooted at `cwd` and relay it
|
|
21
|
+
over the socket. POSIX and Windows need genuinely different pty APIs
|
|
22
|
+
(openpty/fork vs ConPTY), so this just dispatches to whichever this
|
|
23
|
+
process is running on; the platform-only imports (fcntl/pty/termios,
|
|
24
|
+
winpty) happen inside each implementation so neither is ever required
|
|
25
|
+
on the other platform.
|
|
26
|
+
"""
|
|
27
|
+
if sys.platform == "win32":
|
|
28
|
+
await _run_windows_session(websocket, cwd)
|
|
29
|
+
else:
|
|
30
|
+
await _run_posix_session(websocket, cwd)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def _run_posix_session(websocket: WebSocket, cwd: Path) -> None:
|
|
34
|
+
import fcntl
|
|
35
|
+
import pty
|
|
36
|
+
import signal
|
|
37
|
+
import struct
|
|
38
|
+
import termios
|
|
39
|
+
|
|
40
|
+
def _set_winsize(fd: int, rows: int, cols: int) -> None:
|
|
41
|
+
winsize = struct.pack("HHHH", rows, cols, 0, 0)
|
|
42
|
+
fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize)
|
|
43
|
+
|
|
44
|
+
shell = os.environ.get("SHELL", "/bin/bash")
|
|
45
|
+
pid, master_fd = pty.fork()
|
|
46
|
+
|
|
47
|
+
if pid == 0: # child
|
|
48
|
+
os.chdir(cwd)
|
|
49
|
+
os.execvp(shell, [shell])
|
|
50
|
+
os._exit(1) # pragma: no cover - unreachable
|
|
51
|
+
|
|
52
|
+
loop = asyncio.get_running_loop()
|
|
53
|
+
output_queue: asyncio.Queue[bytes | None] = asyncio.Queue()
|
|
54
|
+
|
|
55
|
+
def _on_readable() -> None:
|
|
56
|
+
try:
|
|
57
|
+
data = os.read(master_fd, 65536)
|
|
58
|
+
except OSError:
|
|
59
|
+
data = b""
|
|
60
|
+
if not data:
|
|
61
|
+
loop.remove_reader(master_fd)
|
|
62
|
+
output_queue.put_nowait(None)
|
|
63
|
+
return
|
|
64
|
+
output_queue.put_nowait(data)
|
|
65
|
+
|
|
66
|
+
loop.add_reader(master_fd, _on_readable)
|
|
67
|
+
|
|
68
|
+
async def pump_output() -> None:
|
|
69
|
+
while True:
|
|
70
|
+
chunk = await output_queue.get()
|
|
71
|
+
if chunk is None:
|
|
72
|
+
break
|
|
73
|
+
await websocket.send_text(json.dumps({"type": "output", "data": chunk.decode(errors="replace")}))
|
|
74
|
+
|
|
75
|
+
output_task = asyncio.create_task(pump_output())
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
while True:
|
|
79
|
+
text = await websocket.receive_text()
|
|
80
|
+
try:
|
|
81
|
+
payload = json.loads(text)
|
|
82
|
+
except ValueError:
|
|
83
|
+
continue
|
|
84
|
+
msg_type = payload.get("type")
|
|
85
|
+
if msg_type == "input":
|
|
86
|
+
os.write(master_fd, payload.get("data", "").encode())
|
|
87
|
+
elif msg_type == "resize":
|
|
88
|
+
_set_winsize(master_fd, int(payload.get("rows", 24)), int(payload.get("cols", 80)))
|
|
89
|
+
except WebSocketDisconnect:
|
|
90
|
+
pass
|
|
91
|
+
finally:
|
|
92
|
+
try:
|
|
93
|
+
loop.remove_reader(master_fd)
|
|
94
|
+
except (ValueError, OSError):
|
|
95
|
+
pass
|
|
96
|
+
output_task.cancel()
|
|
97
|
+
try:
|
|
98
|
+
os.kill(pid, signal.SIGHUP)
|
|
99
|
+
except ProcessLookupError:
|
|
100
|
+
pass
|
|
101
|
+
try:
|
|
102
|
+
os.close(master_fd)
|
|
103
|
+
except OSError:
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async def _run_windows_session(websocket: WebSocket, cwd: Path) -> None:
|
|
108
|
+
# Imported lazily so POSIX systems never need this dependency installed,
|
|
109
|
+
# and so a missing install fails inside one terminal connection instead
|
|
110
|
+
# of at process startup (see pyproject.toml: pywinpty is a Windows-only
|
|
111
|
+
# extra, `sys_platform == "win32"`).
|
|
112
|
+
from winpty import PtyProcess
|
|
113
|
+
|
|
114
|
+
shell = os.environ.get("COMSPEC", "cmd.exe")
|
|
115
|
+
process = PtyProcess.spawn(shell, cwd=str(cwd), dimensions=(24, 80))
|
|
116
|
+
|
|
117
|
+
loop = asyncio.get_running_loop()
|
|
118
|
+
output_queue: asyncio.Queue[str | None] = asyncio.Queue()
|
|
119
|
+
|
|
120
|
+
# winpty's read() is a blocking call (there's no fd to hand to the
|
|
121
|
+
# asyncio selector on Windows), so it has to live on its own thread;
|
|
122
|
+
# results are handed back to the event loop via call_soon_threadsafe.
|
|
123
|
+
def _read_loop() -> None:
|
|
124
|
+
while True:
|
|
125
|
+
try:
|
|
126
|
+
data = process.read(65536)
|
|
127
|
+
except EOFError:
|
|
128
|
+
loop.call_soon_threadsafe(output_queue.put_nowait, None)
|
|
129
|
+
return
|
|
130
|
+
if not data:
|
|
131
|
+
loop.call_soon_threadsafe(output_queue.put_nowait, None)
|
|
132
|
+
return
|
|
133
|
+
loop.call_soon_threadsafe(output_queue.put_nowait, data)
|
|
134
|
+
|
|
135
|
+
reader_thread = threading.Thread(target=_read_loop, daemon=True)
|
|
136
|
+
reader_thread.start()
|
|
137
|
+
|
|
138
|
+
async def pump_output() -> None:
|
|
139
|
+
while True:
|
|
140
|
+
chunk = await output_queue.get()
|
|
141
|
+
if chunk is None:
|
|
142
|
+
break
|
|
143
|
+
await websocket.send_text(json.dumps({"type": "output", "data": chunk}))
|
|
144
|
+
|
|
145
|
+
output_task = asyncio.create_task(pump_output())
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
while True:
|
|
149
|
+
text = await websocket.receive_text()
|
|
150
|
+
try:
|
|
151
|
+
payload = json.loads(text)
|
|
152
|
+
except ValueError:
|
|
153
|
+
continue
|
|
154
|
+
msg_type = payload.get("type")
|
|
155
|
+
if msg_type == "input":
|
|
156
|
+
process.write(payload.get("data", ""))
|
|
157
|
+
elif msg_type == "resize":
|
|
158
|
+
process.setwinsize(int(payload.get("rows", 24)), int(payload.get("cols", 80)))
|
|
159
|
+
except WebSocketDisconnect:
|
|
160
|
+
pass
|
|
161
|
+
finally:
|
|
162
|
+
output_task.cancel()
|
|
163
|
+
try:
|
|
164
|
+
process.terminate(force=True)
|
|
165
|
+
except Exception:
|
|
166
|
+
pass
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import fcntl
|
|
5
|
-
import json
|
|
6
|
-
import os
|
|
7
|
-
import pty
|
|
8
|
-
import signal
|
|
9
|
-
import struct
|
|
10
|
-
import termios
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
|
|
13
|
-
from fastapi import WebSocket, WebSocketDisconnect
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _set_winsize(fd: int, rows: int, cols: int) -> None:
|
|
17
|
-
winsize = struct.pack("HHHH", rows, cols, 0, 0)
|
|
18
|
-
fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
async def run_terminal_session(websocket: WebSocket, cwd: Path) -> None:
|
|
22
|
-
"""Spawn a real shell in a PTY rooted at `cwd` and relay it over the socket.
|
|
23
|
-
|
|
24
|
-
Wire protocol (JSON text frames both ways):
|
|
25
|
-
client -> server: {"type": "input", "data": "<keystrokes>"}
|
|
26
|
-
{"type": "resize", "cols": n, "rows": n}
|
|
27
|
-
server -> client: {"type": "output", "data": "<shell output>"}
|
|
28
|
-
"""
|
|
29
|
-
shell = os.environ.get("SHELL", "/bin/bash")
|
|
30
|
-
pid, master_fd = pty.fork()
|
|
31
|
-
|
|
32
|
-
if pid == 0: # child
|
|
33
|
-
os.chdir(cwd)
|
|
34
|
-
os.execvp(shell, [shell])
|
|
35
|
-
os._exit(1) # pragma: no cover - unreachable
|
|
36
|
-
|
|
37
|
-
loop = asyncio.get_running_loop()
|
|
38
|
-
output_queue: asyncio.Queue[bytes | None] = asyncio.Queue()
|
|
39
|
-
|
|
40
|
-
def _on_readable() -> None:
|
|
41
|
-
try:
|
|
42
|
-
data = os.read(master_fd, 65536)
|
|
43
|
-
except OSError:
|
|
44
|
-
data = b""
|
|
45
|
-
if not data:
|
|
46
|
-
loop.remove_reader(master_fd)
|
|
47
|
-
output_queue.put_nowait(None)
|
|
48
|
-
return
|
|
49
|
-
output_queue.put_nowait(data)
|
|
50
|
-
|
|
51
|
-
loop.add_reader(master_fd, _on_readable)
|
|
52
|
-
|
|
53
|
-
async def pump_output() -> None:
|
|
54
|
-
while True:
|
|
55
|
-
chunk = await output_queue.get()
|
|
56
|
-
if chunk is None:
|
|
57
|
-
break
|
|
58
|
-
await websocket.send_text(json.dumps({"type": "output", "data": chunk.decode(errors="replace")}))
|
|
59
|
-
|
|
60
|
-
output_task = asyncio.create_task(pump_output())
|
|
61
|
-
|
|
62
|
-
try:
|
|
63
|
-
while True:
|
|
64
|
-
text = await websocket.receive_text()
|
|
65
|
-
try:
|
|
66
|
-
payload = json.loads(text)
|
|
67
|
-
except ValueError:
|
|
68
|
-
continue
|
|
69
|
-
msg_type = payload.get("type")
|
|
70
|
-
if msg_type == "input":
|
|
71
|
-
os.write(master_fd, payload.get("data", "").encode())
|
|
72
|
-
elif msg_type == "resize":
|
|
73
|
-
_set_winsize(master_fd, int(payload.get("rows", 24)), int(payload.get("cols", 80)))
|
|
74
|
-
except WebSocketDisconnect:
|
|
75
|
-
pass
|
|
76
|
-
finally:
|
|
77
|
-
try:
|
|
78
|
-
loop.remove_reader(master_fd)
|
|
79
|
-
except (ValueError, OSError):
|
|
80
|
-
pass
|
|
81
|
-
output_task.cancel()
|
|
82
|
-
try:
|
|
83
|
-
os.kill(pid, signal.SIGHUP)
|
|
84
|
-
except ProcessLookupError:
|
|
85
|
-
pass
|
|
86
|
-
try:
|
|
87
|
-
os.close(master_fd)
|
|
88
|
-
except OSError:
|
|
89
|
-
pass
|
|
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
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/activity_tracker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/git_resolver.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/process_info.py
RENAMED
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/process_inspector.py
RENAMED
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/backend/git_juggler/agent_tracking/worktree_evidence.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
|
|
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
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Agents/AgentActivityPanel.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Branches/DeleteBranchModal.tsx
RENAMED
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Commits/CommitDetail.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Commits/UncommittedRow.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/CreateTagModal.tsx
RENAMED
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx
RENAMED
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_juggler-0.1.3 → git_juggler-0.1.4}/src/frontend/src/components/Terminal/TerminalPanel.tsx
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
|