shelldeck 0.0.1__py3-none-any.whl
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.
- shelldeck/__init__.py +0 -0
- shelldeck/__main__.py +4 -0
- shelldeck/addons/__init__.py +1 -0
- shelldeck/addons/fast_context.py +1346 -0
- shelldeck/auth.py +207 -0
- shelldeck/cli.py +587 -0
- shelldeck/db.py +794 -0
- shelldeck/gitgraph.py +150 -0
- shelldeck/integration/bash.sh +27 -0
- shelldeck/integration/shelldeck.fish +5 -0
- shelldeck/integration/shelldeck.ps1 +35 -0
- shelldeck/integration/zsh/.zshenv +6 -0
- shelldeck/integration/zsh/.zshrc +10 -0
- shelldeck/pty.py +352 -0
- shelldeck/runner.py +75 -0
- shelldeck/scheduler.py +283 -0
- shelldeck/server.py +1216 -0
- shelldeck/shells.py +174 -0
- shelldeck/static/app.css +2288 -0
- shelldeck/static/app.js +1863 -0
- shelldeck/static/gitgraph.js +142 -0
- shelldeck/static/history.js +172 -0
- shelldeck/static/icon-192.png +0 -0
- shelldeck/static/icon-32.png +0 -0
- shelldeck/static/icon-512.png +0 -0
- shelldeck/static/icon.svg +1 -0
- shelldeck/static/index.html +102 -0
- shelldeck/static/manifest.webmanifest +1 -0
- shelldeck/static/monitor.js +143 -0
- shelldeck/static/ui.js +316 -0
- shelldeck/static/vendor/LICENSE-xterm.txt +21 -0
- shelldeck/static/vendor/addon-fit.js +2 -0
- shelldeck/static/vendor/addon-search.js +2 -0
- shelldeck/static/vendor/addon-serialize.js +2 -0
- shelldeck/static/vendor/addon-web-links.js +2 -0
- shelldeck/static/vendor/addon-webgl.js +2 -0
- shelldeck/static/vendor/xterm.css +218 -0
- shelldeck/static/vendor/xterm.js +2 -0
- shelldeck/static/views.js +658 -0
- shelldeck/stats.py +128 -0
- shelldeck-0.0.1.dist-info/METADATA +254 -0
- shelldeck-0.0.1.dist-info/RECORD +45 -0
- shelldeck-0.0.1.dist-info/WHEEL +4 -0
- shelldeck-0.0.1.dist-info/entry_points.txt +3 -0
- shelldeck-0.0.1.dist-info/licenses/LICENSE +21 -0
shelldeck/stats.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""System and per-terminal resource usage (psutil; GPU via nvidia-smi when present)."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
import psutil
|
|
10
|
+
|
|
11
|
+
NCPU = psutil.cpu_count() or 1
|
|
12
|
+
SMI = shutil.which("nvidia-smi")
|
|
13
|
+
# the detached server has no console; without this each nvidia-smi call flashes a window
|
|
14
|
+
NO_WINDOW = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
|
|
15
|
+
|
|
16
|
+
_procs: dict[int, psutil.Process] = {} # kept between polls so cpu_percent has a baseline
|
|
17
|
+
_gpu: dict = {"at": 0.0, "data": None, "proc": None}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _proc(pid: int) -> psutil.Process:
|
|
21
|
+
p = _procs.get(pid)
|
|
22
|
+
if p is None or not p.is_running():
|
|
23
|
+
p = psutil.Process(pid)
|
|
24
|
+
p.cpu_percent(None)
|
|
25
|
+
_procs[pid] = p
|
|
26
|
+
return p
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _watch_gpu(proc: subprocess.Popen) -> None:
|
|
30
|
+
"""Parse nvidia-smi's own 2s loop; stop it once nobody has asked for 30s."""
|
|
31
|
+
for line in proc.stdout:
|
|
32
|
+
if time.monotonic() - _gpu["at"] > 30:
|
|
33
|
+
break
|
|
34
|
+
try:
|
|
35
|
+
name, util, used, total = [x.strip() for x in line.split(",")]
|
|
36
|
+
_gpu["data"] = {"name": name, "util": float(util), "mem_used": int(used) << 20, "mem_total": int(total) << 20}
|
|
37
|
+
except ValueError:
|
|
38
|
+
continue
|
|
39
|
+
proc.kill()
|
|
40
|
+
_gpu["proc"] = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def stop() -> None:
|
|
44
|
+
if proc := _gpu.get("proc"):
|
|
45
|
+
proc.kill()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def gpu() -> dict | None:
|
|
49
|
+
"""First NVIDIA GPU. ponytail: NVIDIA only; other GPUs show nothing.
|
|
50
|
+
|
|
51
|
+
One long-lived `nvidia-smi -lms` instead of a call per poll: each launch costs ~400ms CPU."""
|
|
52
|
+
if not SMI:
|
|
53
|
+
return None
|
|
54
|
+
_gpu["at"] = time.monotonic()
|
|
55
|
+
if _gpu.get("proc") is None:
|
|
56
|
+
try:
|
|
57
|
+
proc = subprocess.Popen(
|
|
58
|
+
[SMI, "--query-gpu=name,utilization.gpu,memory.used,memory.total", "--format=csv,noheader,nounits", "-lms", "2000"],
|
|
59
|
+
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True, creationflags=NO_WINDOW,
|
|
60
|
+
)
|
|
61
|
+
except OSError:
|
|
62
|
+
return None
|
|
63
|
+
_gpu["proc"] = proc
|
|
64
|
+
threading.Thread(target=_watch_gpu, args=(proc,), name="nvidia-smi", daemon=True).start()
|
|
65
|
+
return _gpu["data"]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def tree(pid: int, seen: set[int]) -> dict | None:
|
|
69
|
+
"""CPU (% of the whole machine), memory and busiest child for a shell and everything it started."""
|
|
70
|
+
try:
|
|
71
|
+
root = _proc(pid)
|
|
72
|
+
procs = [root, *root.children(recursive=True)]
|
|
73
|
+
except psutil.Error:
|
|
74
|
+
return None
|
|
75
|
+
cpu = 0.0
|
|
76
|
+
mem = count = 0
|
|
77
|
+
top: tuple[str, float] | None = None
|
|
78
|
+
pids = []
|
|
79
|
+
for p in procs:
|
|
80
|
+
try:
|
|
81
|
+
p = _proc(p.pid)
|
|
82
|
+
c = p.cpu_percent(None) / NCPU
|
|
83
|
+
m = p.memory_info().rss
|
|
84
|
+
name = p.name()
|
|
85
|
+
except psutil.Error:
|
|
86
|
+
continue
|
|
87
|
+
seen.add(p.pid)
|
|
88
|
+
pids.append(p.pid)
|
|
89
|
+
count += 1
|
|
90
|
+
cpu += c
|
|
91
|
+
mem += m
|
|
92
|
+
if p.pid != pid and (top is None or c >= top[1]):
|
|
93
|
+
top = (name, c)
|
|
94
|
+
return {"cpu": round(cpu, 1), "mem": mem, "procs": count, "top": top[0] if top else None, "pids": pids}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def listening() -> dict[int, set[int]]:
|
|
98
|
+
"""pid -> TCP ports it listens on. One system-wide table per poll (not one per process)."""
|
|
99
|
+
ports: dict[int, set[int]] = {}
|
|
100
|
+
try:
|
|
101
|
+
conns = psutil.net_connections("tcp")
|
|
102
|
+
except (psutil.Error, OSError): # macOS needs root for other users' sockets
|
|
103
|
+
return ports
|
|
104
|
+
for c in conns:
|
|
105
|
+
if c.status == psutil.CONN_LISTEN and c.pid and c.laddr:
|
|
106
|
+
ports.setdefault(c.pid, set()).add(c.laddr.port)
|
|
107
|
+
return ports
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def collect(sessions: dict[str, int]) -> dict:
|
|
111
|
+
"""sessions: session id -> shell pid."""
|
|
112
|
+
seen: set[int] = set()
|
|
113
|
+
per = {sid: t for sid, pid in sessions.items() if (t := tree(pid, seen))}
|
|
114
|
+
listen = listening() if per else {}
|
|
115
|
+
for t in per.values():
|
|
116
|
+
t["ports"] = sorted({port for pid in t.pop("pids") for port in listen.get(pid, ())})
|
|
117
|
+
for pid in [p for p in _procs if p not in seen]:
|
|
118
|
+
del _procs[pid]
|
|
119
|
+
vm = psutil.virtual_memory()
|
|
120
|
+
return {
|
|
121
|
+
"system": {
|
|
122
|
+
"cpu": psutil.cpu_percent(None),
|
|
123
|
+
"mem_used": vm.total - vm.available,
|
|
124
|
+
"mem_total": vm.total,
|
|
125
|
+
"gpu": gpu(),
|
|
126
|
+
},
|
|
127
|
+
"sessions": per,
|
|
128
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: shelldeck
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Project-first multi-terminal for Windows and Linux: real shells, grouped by project, in your browser
|
|
5
|
+
Project-URL: Homepage, https://github.com/codejunction/shelldeck
|
|
6
|
+
Project-URL: Source, https://github.com/codejunction/shelldeck
|
|
7
|
+
Project-URL: Issues, https://github.com/codejunction/shelldeck/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/codejunction/shelldeck/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Abhishek Dutta <phoenixdutta@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: conpty,developer-tools,powershell,pty,terminal,terminal-emulator,wsl,xterm
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Environment :: Web Environment
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: System :: Shells
|
|
24
|
+
Classifier: Topic :: Terminals
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Requires-Dist: apscheduler<4,>=3.10
|
|
27
|
+
Requires-Dist: fastapi>=0.110
|
|
28
|
+
Requires-Dist: psutil>=5.9
|
|
29
|
+
Requires-Dist: pywinpty>=2.0; sys_platform == 'win32'
|
|
30
|
+
Requires-Dist: rich>=13
|
|
31
|
+
Requires-Dist: typer>=0.12
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.29
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
<div align="center">
|
|
36
|
+
|
|
37
|
+
<img src="https://raw.githubusercontent.com/codejunction/shelldeck/main/shelldeck/static/icon.svg" width="88" alt="shelldeck logo">
|
|
38
|
+
|
|
39
|
+
# shelldeck
|
|
40
|
+
|
|
41
|
+
**Your terminals, organised by project. In the browser, on your own machine.**
|
|
42
|
+
|
|
43
|
+
Real shells (PowerShell, cmd, Git Bash, WSL, bash, zsh, fish) grouped by project,<br>
|
|
44
|
+
in split or free-floating panes that survive restarts.
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/shelldeck/)
|
|
47
|
+
[](https://pypi.org/project/shelldeck/)
|
|
48
|
+
[](https://github.com/codejunction/shelldeck/actions/workflows/ci.yml)
|
|
49
|
+
[](#install)
|
|
50
|
+
[](https://github.com/codejunction/shelldeck/blob/main/LICENSE)
|
|
51
|
+
|
|
52
|
+
[Install](#install) · [Features](#features) · [CLI](#cli) · [Remote access](#remote-access) · [Security](#security) · [Contributing](https://github.com/codejunction/shelldeck/blob/main/CONTRIBUTING.md)
|
|
53
|
+
|
|
54
|
+
<img src="https://raw.githubusercontent.com/codejunction/shelldeck/main/docs/assets/screenshot.png" alt="shelldeck with four terminals across three projects: a git log graph, code search results, a Python web server with its port detected, and the terminal list" width="100%">
|
|
55
|
+
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
**Windows** (PowerShell):
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
irm https://raw.githubusercontent.com/codejunction/shelldeck/main/install.ps1 | iex
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Linux** (macOS should work too, but is untested):
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
curl -fsSL https://raw.githubusercontent.com/codejunction/shelldeck/main/install.sh | sh
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The installer sets up [uv](https://docs.astral.sh/uv/) if you don't have it, then installs shelldeck with its own Python 3.12+. Already use Python tooling? Any of these work too:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
uv tool install shelldeck # or: pipx install shelldeck, or: pip install shelldeck
|
|
76
|
+
uvx shelldeck # try it without installing
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Requirements: Windows 10 1809+ (for ConPTY) or Linux, and a modern browser.
|
|
80
|
+
|
|
81
|
+
## Quick start
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
sd # start shelldeck in the background and open it in your browser
|
|
85
|
+
sd ~/code/api # add a folder as a project and open a terminal in it
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`sd` is short for `shelldeck`. The first visit asks you to create a password. After that, every terminal keeps running in the background until you close it, even when the browser tab is closed.
|
|
89
|
+
|
|
90
|
+
## Features
|
|
91
|
+
|
|
92
|
+
### Terminals that feel like editor panes
|
|
93
|
+
|
|
94
|
+
- **Real shells, detected per OS.** On Windows: `pwsh` (default), Windows PowerShell, cmd, Git Bash and WSL (pick a distro). On Linux: every shell in `/etc/shells`, defaulting to your `$SHELL`.
|
|
95
|
+
- **Tiled layout.** Split right or down, drag titles onto edges to split or onto a pane to swap, drag gutters to resize, double-click a title to maximize.
|
|
96
|
+
- **Free layout.** Every terminal becomes a floating window on a scrolling canvas. **Tile all** puts them back in a grid.
|
|
97
|
+
- **Nothing gets lost.** Closing the tab keeps terminals running, and reopening replays their output. After a restart or reboot, each terminal reopens in its last folder with its history above a "restored" marker.
|
|
98
|
+
- **Shell integration** for pwsh, PowerShell, cmd, bash, zsh and fish. Each pane's title shows the last command (red if it failed), `Ctrl+Shift+Up`/`Down` jumps between prompts, and your own prompt (oh-my-posh, starship, …) still loads.
|
|
99
|
+
|
|
100
|
+
### Projects first
|
|
101
|
+
|
|
102
|
+
- **Projects sidebar.** Add folders with the built-in folder browser, then drag to reorder. Each project gets its own color on its sidebar dot, pane border and a subtle background tint.
|
|
103
|
+
- **Git graph.** A branch icon on every git project opens its full commit graph, with branches, merges and tags drawn as colored lanes. Click a branch or tag, or right-click a commit, to check it out.
|
|
104
|
+
- **Clickable paths.** `src/app.py:12:5`, `C:\x\y.ts(3,4)` and Python tracebacks become links that open in VS Code at that line.
|
|
105
|
+
- **Port detection.** Start a dev server and a chip with its port appears on the pane. Click it to open the page.
|
|
106
|
+
|
|
107
|
+
### Built for long sessions
|
|
108
|
+
|
|
109
|
+
- **Command palette** (`Ctrl+Shift+P`) for terminals, bookmarks, layouts and themes.
|
|
110
|
+
- **Command history** with exit codes, durations and folders. Search it and re-run anything.
|
|
111
|
+
- **Search every terminal's output** (`Ctrl+Shift+F`), including terminals that aren't on screen.
|
|
112
|
+
- **Finished-command alerts.** When a long command finishes while you're looking elsewhere, you get a toast, or a desktop notification if you enable them.
|
|
113
|
+
- **Bookmarks** for commands you run often, global or per project.
|
|
114
|
+
- **Scheduler** for cron jobs that run in a project folder, with run history and logs.
|
|
115
|
+
- **Task board** with due dates and reminder alarms.
|
|
116
|
+
- **Task manager** with live CPU, RAM and GPU (NVIDIA) usage for the machine and for each terminal's process tree.
|
|
117
|
+
|
|
118
|
+
<table>
|
|
119
|
+
<tr>
|
|
120
|
+
<td width="50%"><img src="https://raw.githubusercontent.com/codejunction/shelldeck/main/docs/assets/git-graph.png" alt="Git graph popup with branches, merges and tags"></td>
|
|
121
|
+
<td width="50%"><img src="https://raw.githubusercontent.com/codejunction/shelldeck/main/docs/assets/task-manager.png" alt="Task manager with CPU, memory and GPU usage per terminal"></td>
|
|
122
|
+
</tr>
|
|
123
|
+
<tr>
|
|
124
|
+
<td align="center"><sub>Git graph with one-click checkout</sub></td>
|
|
125
|
+
<td align="center"><sub>Task manager, per terminal</sub></td>
|
|
126
|
+
</tr>
|
|
127
|
+
</table>
|
|
128
|
+
|
|
129
|
+
### Secure by default
|
|
130
|
+
|
|
131
|
+
- **Password required.** Every browser logs in separately and locks after 30 minutes idle.
|
|
132
|
+
- **Local only.** shelldeck listens on `127.0.0.1` and refuses requests from other websites.
|
|
133
|
+
- **Remote use** goes over an SSH tunnel or HTTPS. See [Remote access](#remote-access).
|
|
134
|
+
|
|
135
|
+
<details>
|
|
136
|
+
<summary><b>Keyboard shortcuts</b></summary>
|
|
137
|
+
|
|
138
|
+
| Keys | Action |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| `Ctrl+Shift+P` | Command palette (`Ctrl+K` also works outside a terminal) |
|
|
141
|
+
| `Ctrl+Alt+N` | New terminal in the current project |
|
|
142
|
+
| `Ctrl+Alt+\` / `Ctrl+Alt+-` | Split right / down |
|
|
143
|
+
| `Ctrl+Alt+Arrows` | Focus the pane in that direction |
|
|
144
|
+
| `Ctrl+Alt+1…9` | Focus pane 1–9 |
|
|
145
|
+
| `Ctrl+Alt+Enter` | Maximize / restore the pane |
|
|
146
|
+
| `Ctrl+Alt+Q` | Close the focused terminal |
|
|
147
|
+
| `Ctrl+Alt+B` | Bookmark picker (Space selects several, Shift+Enter runs) |
|
|
148
|
+
| `Ctrl+Alt+E` | Toggle the sidebar |
|
|
149
|
+
| `Ctrl+Alt+S` / `Ctrl+Alt+T` | Scheduler / Tasks |
|
|
150
|
+
| `Ctrl+Alt+M` | Task manager |
|
|
151
|
+
| `Ctrl+Alt+R` | Command history |
|
|
152
|
+
| `Ctrl+Shift+F` | Search all terminals |
|
|
153
|
+
| `Ctrl+Alt+,` | Settings |
|
|
154
|
+
| `Ctrl+Alt+L` | Lock |
|
|
155
|
+
| `Ctrl+Alt+H` | Show shortcuts |
|
|
156
|
+
| `Ctrl+C` / `Ctrl+V` | Copy the selection (otherwise sends Ctrl+C) / paste |
|
|
157
|
+
| `Ctrl+F` | Find in the terminal |
|
|
158
|
+
| `Ctrl+Shift+Up` / `Down` | Jump to the previous / next prompt |
|
|
159
|
+
|
|
160
|
+
In the sidebar, Ctrl+click or middle-click a terminal to open it in a split. Right-click a project or terminal for its menu.
|
|
161
|
+
|
|
162
|
+
</details>
|
|
163
|
+
|
|
164
|
+
<details>
|
|
165
|
+
<summary><b>Settings</b></summary>
|
|
166
|
+
|
|
167
|
+
| Setting | Values | Default |
|
|
168
|
+
| --- | --- | --- |
|
|
169
|
+
| Default shell | Windows: pwsh, powershell, cmd, gitbash, wsl · Linux: shells from `/etc/shells` | pwsh / `$SHELL` |
|
|
170
|
+
| WSL distribution | any installed distro, or the system default | system default |
|
|
171
|
+
| Theme | dark, light, system | dark |
|
|
172
|
+
| Terminal font size | 8–32 | 13 |
|
|
173
|
+
| Pane layout | tiled, free | tiled |
|
|
174
|
+
| Project colors | tint on, off | on |
|
|
175
|
+
| Terminal colors | default (follows theme), Dracula, One Dark, Nord, Gruvbox Dark, Solarized Dark, Solarized Light, GitHub Light | default |
|
|
176
|
+
| Terminal font | any installed monospace font; empty uses Cascadia / Nerd Font | empty |
|
|
177
|
+
| Open file paths with | VS Code (at the line), system default app | VS Code |
|
|
178
|
+
| Password | created on first visit, changed here (current + new) | required |
|
|
179
|
+
|
|
180
|
+
</details>
|
|
181
|
+
|
|
182
|
+
## CLI
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
sd [--port N] [--no-window] [--app] start the server if needed, print the banner and open the browser
|
|
186
|
+
sd web same as plain `sd`
|
|
187
|
+
sd PATH shorthand for `sd open PATH`
|
|
188
|
+
sd open [PATH] [--shell wsl] add PATH as a project and open a terminal in it
|
|
189
|
+
sd list terminals grouped by project
|
|
190
|
+
sd info details of the shelldeck terminal you're in
|
|
191
|
+
sd search QUERY [--root DIR] LLM-free code search with ranked, highlighted snippets
|
|
192
|
+
sd render FILE pretty-print code or markdown
|
|
193
|
+
sd schedule list|add|run|toggle|delete|logs
|
|
194
|
+
sd task list|add|move|delete|alarms
|
|
195
|
+
sd serve [--host H] run the server in the foreground
|
|
196
|
+
sd stop stop the background server (closes all terminals)
|
|
197
|
+
sd login-link emergency: one-time login URL (host only)
|
|
198
|
+
sd reset-password emergency: forget the password (host only)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- **Startup banner.** `sd` prints the version and the Local and Network URLs. When you start it from Win+R, the Start menu or a shortcut, its window stays open until you press Enter.
|
|
202
|
+
- **Port.** The default port is `5455`. Change it with `--port` or `SHELLDECK_PORT`.
|
|
203
|
+
- **Browser.** `--app` opens a chromeless Edge/Chrome window instead of a browser tab.
|
|
204
|
+
- **Inside terminals.** Inside a shelldeck terminal, `SHELLDECK_SESSION_ID` is set.
|
|
205
|
+
- **Code search.** `sd search` builds a persistent index and returns ranked snippets with confidence scores. Useful options: `--ext py,ts`, `--glob "src/*"`, `--top N`, `--format text|json|paths` and `--reindex`.
|
|
206
|
+
|
|
207
|
+
## Remote access
|
|
208
|
+
|
|
209
|
+
Run shelldeck on a server or VM and use it from your laptop.
|
|
210
|
+
|
|
211
|
+
**SSH tunnel (recommended).** Nothing is exposed to the network.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
sd serve # on the VM: stays on 127.0.0.1:5455
|
|
215
|
+
ssh -N -L 5455:127.0.0.1:5455 you@vm # on your machine, then open http://127.0.0.1:5455
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**HTTPS on the VM's address,** with a real certificate (Tailscale `tailscale cert`, Let's Encrypt, your reverse proxy) or a self-signed one:
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
sd serve --host 0.0.0.0 --cert cert.pem --key key.pem
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
- **No plain HTTP.** shelldeck refuses plain HTTP on a non-local address, because passwords and terminal traffic would cross the network unencrypted. `--insecure-http` overrides that, for trusted networks only.
|
|
225
|
+
- **First visit.** A first visit from another machine also asks for a **setup code**, which `sd serve` prints on the server.
|
|
226
|
+
- **Dropped connections** (sleep, Wi-Fi change, VPN) reconnect on their own. The terminal redraws from the server's scrollback.
|
|
227
|
+
|
|
228
|
+
## Security
|
|
229
|
+
|
|
230
|
+
- **Local only.** The server binds to `127.0.0.1` and rejects HTTP and WebSocket requests whose `Host` or `Origin` isn't the app itself, so other websites can't reach your shells.
|
|
231
|
+
- **Passwords** are stored as PBKDF2-SHA256 (600k iterations). A login is a random token in an `HttpOnly`, `SameSite=Strict` cookie, and only its SHA-256 is stored. After 5 wrong passwords, each further try waits longer.
|
|
232
|
+
- **The CLI** authenticates with a token file in the data folder, readable only by your account and rotated on every server start.
|
|
233
|
+
- **Forgot the password?** On the host machine, `sd login-link` prints a one-time login URL valid for 5 minutes, and `sd reset-password` removes the password. There is deliberately no way to do either from the browser.
|
|
234
|
+
- **Your data** lives in `~/.config/shelldeck/` (database, log and saved terminal history). Set `SHELLDECK_HOME` to move it.
|
|
235
|
+
|
|
236
|
+
Found a vulnerability? Please report it privately; see [SECURITY.md](https://github.com/codejunction/shelldeck/blob/main/SECURITY.md).
|
|
237
|
+
|
|
238
|
+
## Development
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
git clone https://github.com/codejunction/shelldeck && cd shelldeck
|
|
242
|
+
uv sync
|
|
243
|
+
uv run shelldeck --port 5466 serve # dev server, http://127.0.0.1:5466
|
|
244
|
+
uv run pytest -q # includes a real PTY round trip (ConPTY or POSIX pty)
|
|
245
|
+
uv run ruff check
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
- **Stack.** FastAPI and uvicorn, `pywinpty` (ConPTY) on Windows or the stdlib `pty` elsewhere, and SQLite.
|
|
249
|
+
- **Frontend.** Plain HTML, CSS and JavaScript with vendored xterm.js, and no build step.
|
|
250
|
+
- **Contributing.** See [CONTRIBUTING.md](https://github.com/codejunction/shelldeck/blob/main/CONTRIBUTING.md) for the workflow and release steps.
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
[MIT](https://github.com/codejunction/shelldeck/blob/main/LICENSE). xterm.js is MIT-licensed too (see `shelldeck/static/vendor/LICENSE-xterm.txt`).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
shelldeck/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
shelldeck/__main__.py,sha256=PPb5yrurgGBKz-PlfudjsXc5C1A2oQjhZEm0vnWBnBI,63
|
|
3
|
+
shelldeck/auth.py,sha256=MR2xqCFWL7_pp1virubCXssG_QPG0sHXzN18tl6QHV8,6402
|
|
4
|
+
shelldeck/cli.py,sha256=T0gY_KF5X__tMNiepfxG2Xjz3BnqfMjCoApTFmJlMV4,22636
|
|
5
|
+
shelldeck/db.py,sha256=D84pkaHBDOHD4YahyHn7KeSJ-PYbpsHaiM2CvLwPUOs,26392
|
|
6
|
+
shelldeck/gitgraph.py,sha256=2qnrU7l2zFngBQN4L-zuzc3QUErL2NMP9Ny1BsV9NcI,5529
|
|
7
|
+
shelldeck/pty.py,sha256=bZ_jhL9kT0o6g4HLJUzrMHcoLDcf84E5dAEBVG9q8Xo,12745
|
|
8
|
+
shelldeck/runner.py,sha256=kcfFmfCQmsmyDs0m6TNobQmbSz3tTajAa7jgTcOpiEk,2092
|
|
9
|
+
shelldeck/scheduler.py,sha256=3EQao9gn0O5H3CVTm-fasMcIZme090W24Biyqkho9ko,7482
|
|
10
|
+
shelldeck/server.py,sha256=TZZfpgwvmE_hF-UC9oI2CJvYncSW6MfwKX-j3VvKJH0,42543
|
|
11
|
+
shelldeck/shells.py,sha256=HK7a_j63fLGhMxCle27Wg6YNpOqcKmf8LHvVEujWND4,6253
|
|
12
|
+
shelldeck/stats.py,sha256=ly75SRThM9WkfAALSZaO8201IBXWQxY9RVyJLdohUC0,4242
|
|
13
|
+
shelldeck/addons/__init__.py,sha256=L_2MaGOBA4zLgyuMfQeo2TzaA29jLmvtdLY0Z0NBR08,28
|
|
14
|
+
shelldeck/addons/fast_context.py,sha256=7NJR2xD7uN-lsmqMDNAa86FUJdZNwelcb5csNvPK8M8,60127
|
|
15
|
+
shelldeck/integration/bash.sh,sha256=sFOs9QpJd21OPut_fe-Byi_SizB9I1YRlLuDzNdP4xM,913
|
|
16
|
+
shelldeck/integration/shelldeck.fish,sha256=id1BqniHWibDuJcoWh3e-111SkSs1epC0u9m1eVtO9Y,215
|
|
17
|
+
shelldeck/integration/shelldeck.ps1,sha256=nYnTdliJMM1yMgmtA8nMv-ByU0TY52DsPWLe7mPQopU,1474
|
|
18
|
+
shelldeck/integration/zsh/.zshenv,sha256=BNi-RNfovsipW-QH6TEKsC55gKINa1XVZE0ikw1V0pI,264
|
|
19
|
+
shelldeck/integration/zsh/.zshrc,sha256=eZlW2ApzW8DGHqTTVw5LGHiyVIlNGxb6E-l3EVulGgI,337
|
|
20
|
+
shelldeck/static/app.css,sha256=1fBWDYykEvFeoLcw1tXivgx607Wfz2cNZaFkhOZXkZA,35668
|
|
21
|
+
shelldeck/static/app.js,sha256=bdBTRcC0mvpaEcETlCN4SxiLwNjnjquRmrgkuSGSzHg,70719
|
|
22
|
+
shelldeck/static/gitgraph.js,sha256=WK8KRZKQYgidklA1mZ5S_z4pT9oUn-zuvmx8yPkaO7A,5146
|
|
23
|
+
shelldeck/static/history.js,sha256=uwNRlbQXsSwgQnw3jrf-Td5DApHgmsTMcDA1KhwwDMY,7792
|
|
24
|
+
shelldeck/static/icon-192.png,sha256=9r0FdZSOAgu31kk7JntPlAN2p4WMb1sxNff2aR_PI8M,4792
|
|
25
|
+
shelldeck/static/icon-32.png,sha256=u4W05ook-hz-nKvv2SVfziT9WOW0IJc0-2LzheNq35w,762
|
|
26
|
+
shelldeck/static/icon-512.png,sha256=YlQ3EH1nGqPeylAAYZrohpT4__dLYwu-rlKCIszdK2I,16791
|
|
27
|
+
shelldeck/static/icon.svg,sha256=EOihBPCC9r4H7nxjNqS5AZdD0ybOldRCw2p8JTDUhks,347
|
|
28
|
+
shelldeck/static/index.html,sha256=bWXs6dvpScTXOD1-IyCNZvAnVajUnMzAELRLR_qQx5o,6222
|
|
29
|
+
shelldeck/static/manifest.webmanifest,sha256=Q-UdX21jaG8cUVESGy4xT0AOatI7u2eVvR1b7Unu_sM,364
|
|
30
|
+
shelldeck/static/monitor.js,sha256=SzbkUn6iBmbFgxHHZo0E5oUtLxI0cCqY37MtMg7E8ZA,6525
|
|
31
|
+
shelldeck/static/ui.js,sha256=Xj7QvSLZMX7tCYci-g7nqWik-5T5ipnIHwKiQd0GgEI,13478
|
|
32
|
+
shelldeck/static/views.js,sha256=zqRNlcw7Py6gRnUw0qDMjcgsaoYsBIl2jYnxCTNW0xs,30004
|
|
33
|
+
shelldeck/static/vendor/LICENSE-xterm.txt,sha256=tWn2KdAPJiaoEA3yoXmCEFNWIeQhZN_UJqb-Wqx7DM0,1261
|
|
34
|
+
shelldeck/static/vendor/addon-fit.js,sha256=va76Nwsb_ELuiNRv5gckAJAqTUstRc2TQ43amyPJcIk,1497
|
|
35
|
+
shelldeck/static/vendor/addon-search.js,sha256=PPUtcdnetLpgElCHQ0xT4_s1uyJJ25sTmHmR_S2xx70,12067
|
|
36
|
+
shelldeck/static/vendor/addon-serialize.js,sha256=_EMjWaAvdIK9zNe4rEbckXwtBepUBzeQDe2NnNFQtjM,25210
|
|
37
|
+
shelldeck/static/vendor/addon-web-links.js,sha256=8jCmyCEc5GFN2lRB8ntgPHwcqVFRplW8DvrGN37mQ_A,3090
|
|
38
|
+
shelldeck/static/vendor/addon-webgl.js,sha256=n_qaw_9tR9TmIW7Rlyyo4LUzbO90T1Dr3D9nsO1yfNs,100856
|
|
39
|
+
shelldeck/static/vendor/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
|
|
40
|
+
shelldeck/static/vendor/xterm.js,sha256=H5kaw7Syg-v5bmCuI6AKUnZd06Lkb6b92p8aqwMvdJU,289441
|
|
41
|
+
shelldeck-0.0.1.dist-info/METADATA,sha256=j_V82EUoZ5lSJ_uTGu6qH41PurVPKDDPzccYMmj3Uyc,13824
|
|
42
|
+
shelldeck-0.0.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
43
|
+
shelldeck-0.0.1.dist-info/entry_points.txt,sha256=jQb8I0T5DdeNrIENVq3vsNPRjIf3FYeaxXBi2DaUjcs,75
|
|
44
|
+
shelldeck-0.0.1.dist-info/licenses/LICENSE,sha256=xhrQs9vjvDpIAPIcOYTcq7TilFDXVVUnWJC9K2JO-WE,1071
|
|
45
|
+
shelldeck-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhishek Dutta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|