sshpeek 0.1.0__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.
- sshpeek-0.1.0/.claude/settings.local.json +8 -0
- sshpeek-0.1.0/.github/workflows/ci.yml +12 -0
- sshpeek-0.1.0/.github/workflows/release.yml +17 -0
- sshpeek-0.1.0/.gitignore +10 -0
- sshpeek-0.1.0/LICENSE +21 -0
- sshpeek-0.1.0/PKG-INFO +170 -0
- sshpeek-0.1.0/README.md +118 -0
- sshpeek-0.1.0/pyproject.toml +47 -0
- sshpeek-0.1.0/src/sshpeek/__init__.py +3 -0
- sshpeek-0.1.0/src/sshpeek/__main__.py +38 -0
- sshpeek-0.1.0/src/sshpeek/app.py +400 -0
- sshpeek-0.1.0/src/sshpeek/config.py +160 -0
- sshpeek-0.1.0/src/sshpeek/pool.py +175 -0
- sshpeek-0.1.0/src/sshpeek/proxy.py +199 -0
- sshpeek-0.1.0/src/sshpeek/s3.py +110 -0
- sshpeek-0.1.0/src/sshpeek/static/index.html +535 -0
- sshpeek-0.1.0/src/sshpeek/static/viewer.html +212 -0
- sshpeek-0.1.0/sshpeek.example.yaml +42 -0
- sshpeek-0.1.0/tests/test_basic.py +88 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ["v*"]
|
|
5
|
+
jobs:
|
|
6
|
+
pypi:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write # PyPI trusted publishing, no token needed
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: actions/setup-python@v5
|
|
13
|
+
with:
|
|
14
|
+
python-version: "3.12"
|
|
15
|
+
- run: pip install build
|
|
16
|
+
- run: python -m build
|
|
17
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
sshpeek-0.1.0/.gitignore
ADDED
sshpeek-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thibaut Lamadon
|
|
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.
|
sshpeek-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sshpeek
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local web UI for peeking at remote files and ports over SSH
|
|
5
|
+
Project-URL: Homepage, https://github.com/tlamadon/sshpeek
|
|
6
|
+
Project-URL: Issues, https://github.com/tlamadon/sshpeek/issues
|
|
7
|
+
Author: Thibaut Lamadon
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Thibaut Lamadon
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: pdf,port-forward,s3,sftp,ssh,tunnel,web-ui
|
|
31
|
+
Classifier: Development Status :: 4 - Beta
|
|
32
|
+
Classifier: Environment :: Web Environment
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: Intended Audience :: Science/Research
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
40
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
41
|
+
Classifier: Topic :: System :: Networking
|
|
42
|
+
Requires-Python: >=3.11
|
|
43
|
+
Requires-Dist: asyncssh>=2.14
|
|
44
|
+
Requires-Dist: httpx>=0.27
|
|
45
|
+
Requires-Dist: pyyaml>=6
|
|
46
|
+
Requires-Dist: starlette>=0.37
|
|
47
|
+
Requires-Dist: uvicorn>=0.29
|
|
48
|
+
Requires-Dist: websockets>=13
|
|
49
|
+
Provides-Extra: s3
|
|
50
|
+
Requires-Dist: boto3>=1.34; extra == 's3'
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
|
|
53
|
+
# sshpeek
|
|
54
|
+
|
|
55
|
+
A small local web UI for peeking at remote machines over SSH: browse and view
|
|
56
|
+
files, live-view PDFs and images that auto-refresh when they change remotely
|
|
57
|
+
(remote LaTeX builds, plotting pipelines), open port tunnels, reach named
|
|
58
|
+
remote web apps at stable `*.localhost` URLs, and browse S3 buckets alongside.
|
|
59
|
+
|
|
60
|
+
One multiplexed SSH connection per host, kept alive in the background.
|
|
61
|
+
Everything that works with plain `ssh <host>` works here: hosts, keys,
|
|
62
|
+
ProxyJump, agents all come from `~/.ssh/config` and your ssh-agent.
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
pip install sshpeek # or: uv tool install sshpeek
|
|
68
|
+
pip install 'sshpeek[s3]' # with S3 support
|
|
69
|
+
sshpeek # → http://127.0.0.1:8642
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or straight from the repo without installing: `uv run sshpeek`.
|
|
73
|
+
|
|
74
|
+
Zero config works (type any `~/.ssh/config` alias in the host box), but the
|
|
75
|
+
point of sshpeek is the declarative config. Copy `sshpeek.example.yaml` to
|
|
76
|
+
`./sshpeek.yaml` or `~/.config/sshpeek/sshpeek.yaml` (or point `$SSHPEEK_CONFIG` /
|
|
77
|
+
`--config` at it):
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
listen: { host: 127.0.0.1, port: 8642 }
|
|
81
|
+
|
|
82
|
+
hosts:
|
|
83
|
+
mercury:
|
|
84
|
+
tunnels:
|
|
85
|
+
- 5432 # 127.0.0.1:5432 -> mercury localhost:5432
|
|
86
|
+
- local: 18888
|
|
87
|
+
remote: localhost:8888
|
|
88
|
+
http:
|
|
89
|
+
jupyter: 8888 # http://jupyter.mercury.localhost:8642/
|
|
90
|
+
mlflow: localhost:5000
|
|
91
|
+
homebox:
|
|
92
|
+
http:
|
|
93
|
+
grafana: 3000
|
|
94
|
+
internultra: # just a host chip for browsing
|
|
95
|
+
|
|
96
|
+
s3:
|
|
97
|
+
data: my-bucket # chip "data" -> bucket "my-bucket"
|
|
98
|
+
results:
|
|
99
|
+
bucket: my-results-bucket
|
|
100
|
+
prefix: runs/ # browse only under this key prefix
|
|
101
|
+
profile: work # ~/.aws profile; SSO works too
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Declared tunnels and services are established at startup and re-ensured
|
|
105
|
+
every 20s, so they self-heal after connection drops or laptop sleep
|
|
106
|
+
(re-binding the same local ports).
|
|
107
|
+
|
|
108
|
+
## What it does
|
|
109
|
+
|
|
110
|
+
- **Files** — browse remote directories (sftp over the shared connection),
|
|
111
|
+
view text/logs/PDFs inline, download anything. File-type icons, and a
|
|
112
|
+
toggle for dotfiles (hidden by default).
|
|
113
|
+
- **Live views** — the `live` link next to any PDF or image opens a viewer
|
|
114
|
+
that watches the file over SSE and re-renders when it changes, keeping
|
|
115
|
+
your scroll position. PDF hyperlinks (URLs, citations, TOC) are clickable.
|
|
116
|
+
Refreshes are debounced until the file size is stable across two polls,
|
|
117
|
+
so a `latexmk` mid-write never renders garbage. A "Live views" panel
|
|
118
|
+
lists every open view and can detach one remotely — its tab closes
|
|
119
|
+
itself, handy for trimming after a long session.
|
|
120
|
+
- **S3 sources** — buckets declared under `s3:` appear next to the SSH
|
|
121
|
+
hosts: same browsing, peeking, downloads and live views, with keys
|
|
122
|
+
presented as paths. Credentials use the normal boto3 chain (env vars,
|
|
123
|
+
profiles, SSO). Optional: `pip install 'sshpeek[s3]'`.
|
|
124
|
+
- **Tunnels** — raw TCP binds on `127.0.0.1` to any remote port through the
|
|
125
|
+
SSH connection: databases, dashboards, anything. Declared in YAML (pinned)
|
|
126
|
+
or opened ad hoc from the UI.
|
|
127
|
+
- **HTTP services** — named remote web apps proxied by sshpeek itself under
|
|
128
|
+
`<name>.<host>.localhost:<port>`. Browsers resolve `*.localhost` to
|
|
129
|
+
loopback natively, so the URLs are bookmarkable with no `/etc/hosts`
|
|
130
|
+
edits. Each service lives at the root of its own origin, so apps that
|
|
131
|
+
generate absolute paths (Jupyter, Grafana, ...) work unmodified, cookies
|
|
132
|
+
stay isolated per service, and WebSockets are proxied too.
|
|
133
|
+
|
|
134
|
+
Tunnels vs. services: a tunnel gives you a local TCP port (use for anything
|
|
135
|
+
non-HTTP, or when the app itself must see `127.0.0.1`); a service gives you
|
|
136
|
+
a stable named URL through sshpeek's proxy. Both ride the same SSH connection.
|
|
137
|
+
|
|
138
|
+
## Notes
|
|
139
|
+
|
|
140
|
+
- Host keys are checked against `~/.ssh/known_hosts`; if you have never
|
|
141
|
+
ssh'd to a host from this machine, do that once first.
|
|
142
|
+
- Binds to 127.0.0.1 by default. Anything running locally can reach the API
|
|
143
|
+
and therefore your remote files — same trust model as your ssh-agent.
|
|
144
|
+
- `*.localhost` resolution is native in Chrome/Firefox/Safari and
|
|
145
|
+
systemd-resolved; curl needs `--resolve` or a hosts entry.
|
|
146
|
+
- `--port` / `--host` / `--config` / `-v` flags on the CLI.
|
|
147
|
+
|
|
148
|
+
## API
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
GET /api/hosts configured + connected hosts
|
|
152
|
+
GET /api/services declared HTTP services + proxy URLs
|
|
153
|
+
GET /api/ls?host=&path= directory listing (default: remote $HOME)
|
|
154
|
+
GET /api/file?host=&path=[&dl=1] stream file bytes
|
|
155
|
+
GET /api/events?host=&path= SSE change events (stable-size debounced)
|
|
156
|
+
GET /api/views open live views (one per events stream)
|
|
157
|
+
DELETE /api/views/{id} detach a live view (its tab closes itself)
|
|
158
|
+
GET /api/forwards list tunnels
|
|
159
|
+
POST /api/forwards {"host": "mercury", "port": 8888, "local": 18888}
|
|
160
|
+
DELETE /api/forwards/{id}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Roadmap
|
|
164
|
+
|
|
165
|
+
- Log tailing: `/api/file` with an offset param + incremental fetch,
|
|
166
|
+
rendered as a live `<pre>` — cheap `tail -f` for Slurm `.out` files.
|
|
167
|
+
- Browser terminal: xterm.js over a WebSocket to an SSH PTY.
|
|
168
|
+
- Centralized deployment: containerize behind Caddy + Cloudflare Access;
|
|
169
|
+
the subdomain proxy pattern carries over (real subdomains instead of
|
|
170
|
+
`*.localhost`), tunnels become the piece that stays per-machine.
|
sshpeek-0.1.0/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# sshpeek
|
|
2
|
+
|
|
3
|
+
A small local web UI for peeking at remote machines over SSH: browse and view
|
|
4
|
+
files, live-view PDFs and images that auto-refresh when they change remotely
|
|
5
|
+
(remote LaTeX builds, plotting pipelines), open port tunnels, reach named
|
|
6
|
+
remote web apps at stable `*.localhost` URLs, and browse S3 buckets alongside.
|
|
7
|
+
|
|
8
|
+
One multiplexed SSH connection per host, kept alive in the background.
|
|
9
|
+
Everything that works with plain `ssh <host>` works here: hosts, keys,
|
|
10
|
+
ProxyJump, agents all come from `~/.ssh/config` and your ssh-agent.
|
|
11
|
+
|
|
12
|
+
## Quickstart
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pip install sshpeek # or: uv tool install sshpeek
|
|
16
|
+
pip install 'sshpeek[s3]' # with S3 support
|
|
17
|
+
sshpeek # → http://127.0.0.1:8642
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or straight from the repo without installing: `uv run sshpeek`.
|
|
21
|
+
|
|
22
|
+
Zero config works (type any `~/.ssh/config` alias in the host box), but the
|
|
23
|
+
point of sshpeek is the declarative config. Copy `sshpeek.example.yaml` to
|
|
24
|
+
`./sshpeek.yaml` or `~/.config/sshpeek/sshpeek.yaml` (or point `$SSHPEEK_CONFIG` /
|
|
25
|
+
`--config` at it):
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
listen: { host: 127.0.0.1, port: 8642 }
|
|
29
|
+
|
|
30
|
+
hosts:
|
|
31
|
+
mercury:
|
|
32
|
+
tunnels:
|
|
33
|
+
- 5432 # 127.0.0.1:5432 -> mercury localhost:5432
|
|
34
|
+
- local: 18888
|
|
35
|
+
remote: localhost:8888
|
|
36
|
+
http:
|
|
37
|
+
jupyter: 8888 # http://jupyter.mercury.localhost:8642/
|
|
38
|
+
mlflow: localhost:5000
|
|
39
|
+
homebox:
|
|
40
|
+
http:
|
|
41
|
+
grafana: 3000
|
|
42
|
+
internultra: # just a host chip for browsing
|
|
43
|
+
|
|
44
|
+
s3:
|
|
45
|
+
data: my-bucket # chip "data" -> bucket "my-bucket"
|
|
46
|
+
results:
|
|
47
|
+
bucket: my-results-bucket
|
|
48
|
+
prefix: runs/ # browse only under this key prefix
|
|
49
|
+
profile: work # ~/.aws profile; SSO works too
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Declared tunnels and services are established at startup and re-ensured
|
|
53
|
+
every 20s, so they self-heal after connection drops or laptop sleep
|
|
54
|
+
(re-binding the same local ports).
|
|
55
|
+
|
|
56
|
+
## What it does
|
|
57
|
+
|
|
58
|
+
- **Files** — browse remote directories (sftp over the shared connection),
|
|
59
|
+
view text/logs/PDFs inline, download anything. File-type icons, and a
|
|
60
|
+
toggle for dotfiles (hidden by default).
|
|
61
|
+
- **Live views** — the `live` link next to any PDF or image opens a viewer
|
|
62
|
+
that watches the file over SSE and re-renders when it changes, keeping
|
|
63
|
+
your scroll position. PDF hyperlinks (URLs, citations, TOC) are clickable.
|
|
64
|
+
Refreshes are debounced until the file size is stable across two polls,
|
|
65
|
+
so a `latexmk` mid-write never renders garbage. A "Live views" panel
|
|
66
|
+
lists every open view and can detach one remotely — its tab closes
|
|
67
|
+
itself, handy for trimming after a long session.
|
|
68
|
+
- **S3 sources** — buckets declared under `s3:` appear next to the SSH
|
|
69
|
+
hosts: same browsing, peeking, downloads and live views, with keys
|
|
70
|
+
presented as paths. Credentials use the normal boto3 chain (env vars,
|
|
71
|
+
profiles, SSO). Optional: `pip install 'sshpeek[s3]'`.
|
|
72
|
+
- **Tunnels** — raw TCP binds on `127.0.0.1` to any remote port through the
|
|
73
|
+
SSH connection: databases, dashboards, anything. Declared in YAML (pinned)
|
|
74
|
+
or opened ad hoc from the UI.
|
|
75
|
+
- **HTTP services** — named remote web apps proxied by sshpeek itself under
|
|
76
|
+
`<name>.<host>.localhost:<port>`. Browsers resolve `*.localhost` to
|
|
77
|
+
loopback natively, so the URLs are bookmarkable with no `/etc/hosts`
|
|
78
|
+
edits. Each service lives at the root of its own origin, so apps that
|
|
79
|
+
generate absolute paths (Jupyter, Grafana, ...) work unmodified, cookies
|
|
80
|
+
stay isolated per service, and WebSockets are proxied too.
|
|
81
|
+
|
|
82
|
+
Tunnels vs. services: a tunnel gives you a local TCP port (use for anything
|
|
83
|
+
non-HTTP, or when the app itself must see `127.0.0.1`); a service gives you
|
|
84
|
+
a stable named URL through sshpeek's proxy. Both ride the same SSH connection.
|
|
85
|
+
|
|
86
|
+
## Notes
|
|
87
|
+
|
|
88
|
+
- Host keys are checked against `~/.ssh/known_hosts`; if you have never
|
|
89
|
+
ssh'd to a host from this machine, do that once first.
|
|
90
|
+
- Binds to 127.0.0.1 by default. Anything running locally can reach the API
|
|
91
|
+
and therefore your remote files — same trust model as your ssh-agent.
|
|
92
|
+
- `*.localhost` resolution is native in Chrome/Firefox/Safari and
|
|
93
|
+
systemd-resolved; curl needs `--resolve` or a hosts entry.
|
|
94
|
+
- `--port` / `--host` / `--config` / `-v` flags on the CLI.
|
|
95
|
+
|
|
96
|
+
## API
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
GET /api/hosts configured + connected hosts
|
|
100
|
+
GET /api/services declared HTTP services + proxy URLs
|
|
101
|
+
GET /api/ls?host=&path= directory listing (default: remote $HOME)
|
|
102
|
+
GET /api/file?host=&path=[&dl=1] stream file bytes
|
|
103
|
+
GET /api/events?host=&path= SSE change events (stable-size debounced)
|
|
104
|
+
GET /api/views open live views (one per events stream)
|
|
105
|
+
DELETE /api/views/{id} detach a live view (its tab closes itself)
|
|
106
|
+
GET /api/forwards list tunnels
|
|
107
|
+
POST /api/forwards {"host": "mercury", "port": 8888, "local": 18888}
|
|
108
|
+
DELETE /api/forwards/{id}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Roadmap
|
|
112
|
+
|
|
113
|
+
- Log tailing: `/api/file` with an offset param + incremental fetch,
|
|
114
|
+
rendered as a live `<pre>` — cheap `tail -f` for Slurm `.out` files.
|
|
115
|
+
- Browser terminal: xterm.js over a WebSocket to an SSH PTY.
|
|
116
|
+
- Centralized deployment: containerize behind Caddy + Cloudflare Access;
|
|
117
|
+
the subdomain proxy pattern carries over (real subdomains instead of
|
|
118
|
+
`*.localhost`), tunnels become the piece that stays per-machine.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sshpeek"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Local web UI for peeking at remote files and ports over SSH"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { file = "LICENSE" }
|
|
7
|
+
authors = [{ name = "Thibaut Lamadon" }]
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
keywords = ["ssh", "sftp", "tunnel", "port-forward", "pdf", "s3", "web-ui"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Environment :: Web Environment",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Programming Language :: Python :: 3.14",
|
|
20
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
21
|
+
"Topic :: System :: Networking",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"asyncssh>=2.14",
|
|
25
|
+
"starlette>=0.37",
|
|
26
|
+
"uvicorn>=0.29",
|
|
27
|
+
"pyyaml>=6",
|
|
28
|
+
"httpx>=0.27",
|
|
29
|
+
"websockets>=13",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/tlamadon/sshpeek"
|
|
34
|
+
Issues = "https://github.com/tlamadon/sshpeek/issues"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
s3 = ["boto3>=1.34"]
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
sshpeek = "sshpeek.__main__:main"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["hatchling"]
|
|
44
|
+
build-backend = "hatchling.build"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/sshpeek"]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import uvicorn
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> None:
|
|
10
|
+
from .config import load_config
|
|
11
|
+
|
|
12
|
+
cfg = load_config()
|
|
13
|
+
ap = argparse.ArgumentParser(
|
|
14
|
+
prog="sshpeek",
|
|
15
|
+
description="Local web UI for peeking at remote files and ports over SSH.",
|
|
16
|
+
)
|
|
17
|
+
ap.add_argument("--host", default=cfg.listen_host,
|
|
18
|
+
help=f"bind address (default: {cfg.listen_host})")
|
|
19
|
+
ap.add_argument("--port", type=int, default=cfg.listen_port,
|
|
20
|
+
help=f"bind port (default: {cfg.listen_port})")
|
|
21
|
+
ap.add_argument("--config", help="path to sshpeek.yaml (also: $SSHPEEK_CONFIG)")
|
|
22
|
+
ap.add_argument("-v", "--verbose", action="store_true", help="debug logging")
|
|
23
|
+
args = ap.parse_args()
|
|
24
|
+
|
|
25
|
+
if args.config:
|
|
26
|
+
import os
|
|
27
|
+
os.environ["SSHPEEK_CONFIG"] = args.config
|
|
28
|
+
|
|
29
|
+
logging.basicConfig(
|
|
30
|
+
level=logging.DEBUG if args.verbose else logging.INFO,
|
|
31
|
+
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
|
32
|
+
)
|
|
33
|
+
print(f"sshpeek: http://{args.host}:{args.port}")
|
|
34
|
+
uvicorn.run("sshpeek.app:app", host=args.host, port=args.port, log_level="warning")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|