sshler 0.3.2__tar.gz → 0.3.3__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.
Files changed (44) hide show
  1. {sshler-0.3.2/sshler.egg-info → sshler-0.3.3}/PKG-INFO +123 -49
  2. {sshler-0.3.2 → sshler-0.3.3}/README.md +125 -52
  3. {sshler-0.3.2 → sshler-0.3.3}/pyproject.toml +2 -1
  4. {sshler-0.3.2 → sshler-0.3.3}/sshler/static/base.js +101 -2
  5. sshler-0.3.3/sshler/static/favicon-terminal-local.svg +8 -0
  6. {sshler-0.3.2 → sshler-0.3.3}/sshler/static/style.css +16 -1
  7. {sshler-0.3.2 → sshler-0.3.3}/sshler/static/term.js +199 -10
  8. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/box.html +1 -1
  9. sshler-0.3.3/sshler/templates/docs.html +20 -0
  10. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/index.html +16 -11
  11. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/partials/dir_listing.html +8 -7
  12. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/term.html +16 -15
  13. {sshler-0.3.2 → sshler-0.3.3}/sshler/webapp.py +54 -10
  14. {sshler-0.3.2 → sshler-0.3.3/sshler.egg-info}/PKG-INFO +123 -49
  15. {sshler-0.3.2 → sshler-0.3.3}/sshler.egg-info/SOURCES.txt +1 -0
  16. {sshler-0.3.2 → sshler-0.3.3}/sshler.egg-info/requires.txt +1 -0
  17. {sshler-0.3.2 → sshler-0.3.3}/tests/test_routes.py +40 -0
  18. sshler-0.3.2/sshler/templates/docs.html +0 -40
  19. {sshler-0.3.2 → sshler-0.3.3}/MANIFEST.in +0 -0
  20. {sshler-0.3.2 → sshler-0.3.3}/setup.cfg +0 -0
  21. {sshler-0.3.2 → sshler-0.3.3}/sshler/__init__.py +0 -0
  22. {sshler-0.3.2 → sshler-0.3.3}/sshler/cli.py +0 -0
  23. {sshler-0.3.2 → sshler-0.3.3}/sshler/config.py +0 -0
  24. {sshler-0.3.2 → sshler-0.3.3}/sshler/scripts/install-sshler-task.ps1 +0 -0
  25. {sshler-0.3.2 → sshler-0.3.3}/sshler/scripts/remove-sshler-task.ps1 +0 -0
  26. {sshler-0.3.2 → sshler-0.3.3}/sshler/scripts/run-sshler.ps1 +0 -0
  27. {sshler-0.3.2 → sshler-0.3.3}/sshler/ssh.py +0 -0
  28. {sshler-0.3.2 → sshler-0.3.3}/sshler/ssh_config.py +0 -0
  29. {sshler-0.3.2 → sshler-0.3.3}/sshler/state.py +0 -0
  30. {sshler-0.3.2 → sshler-0.3.3}/sshler/static/favicon-terminal.svg +0 -0
  31. {sshler-0.3.2 → sshler-0.3.3}/sshler/static/favicon.svg +0 -0
  32. {sshler-0.3.2 → sshler-0.3.3}/sshler/static/file-edit.js +0 -0
  33. {sshler-0.3.2 → sshler-0.3.3}/sshler/static/file-view.js +0 -0
  34. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/base.html +0 -0
  35. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/file_edit.html +0 -0
  36. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/file_view.html +0 -0
  37. {sshler-0.3.2 → sshler-0.3.3}/sshler/templates/new_box.html +0 -0
  38. {sshler-0.3.2 → sshler-0.3.3}/sshler.egg-info/dependency_links.txt +0 -0
  39. {sshler-0.3.2 → sshler-0.3.3}/sshler.egg-info/entry_points.txt +0 -0
  40. {sshler-0.3.2 → sshler-0.3.3}/sshler.egg-info/top_level.txt +0 -0
  41. {sshler-0.3.2 → sshler-0.3.3}/tests/test_basic.py +0 -0
  42. {sshler-0.3.2 → sshler-0.3.3}/tests/test_config.py +0 -0
  43. {sshler-0.3.2 → sshler-0.3.3}/tests/test_ssh.py +0 -0
  44. {sshler-0.3.2 → sshler-0.3.3}/tests/test_websocket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sshler
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: A local FastAPI-powered SSH multiplexer for tmux-in-browser — from your laptop only.
5
5
  Author: You
6
6
  License-Expression: MIT
@@ -30,6 +30,7 @@ Requires-Dist: asyncssh>=2.14.0
30
30
  Requires-Dist: platformdirs>=4.2
31
31
  Requires-Dist: pydantic>=2.7
32
32
  Requires-Dist: python-multipart>=0.0.9
33
+ Requires-Dist: markdown-it-py>=3.0.0
33
34
  Requires-Dist: sqler>=1.2025.9.24
34
35
  Provides-Extra: dev
35
36
  Requires-Dist: anyio>=4.4; extra == "dev"
@@ -38,60 +39,42 @@ Requires-Dist: pytest>=8.2; extra == "dev"
38
39
  Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
39
40
  Requires-Dist: ruff>=0.5.6; extra == "dev"
40
41
 
42
+ # sshler
41
43
 
42
- # sshler / エスエスエイチラー
44
+ sshler is a lightweight, local-only web UI that lets you browse remote files over SFTP and jump into tmux sessions in your browser — without installing anything on the remote host.
43
45
 
44
- **English:** sshler is a lightweight, local-only web UI that lets you browse remote
45
- files over SFTP and jump into tmux sessions in your browser — without installing
46
- anything on the remote host.
46
+ ## Features
47
47
 
48
- **日本語:** sshler はローカル専用の軽量 Web UI で、リモートファイルを SFTP で閲覧したり、
49
- ブラウザ上で tmux セッションに接続したりできます。リモート側に追加ソフトを
50
- インストールする必要はありません。
48
+ - **Cross-platform**: Runs on Windows 11, macOS, and Linux (anywhere with Python 3.12+)
49
+ - **Local workspace**: Browse your own filesystem and launch native tmux sessions alongside remote hosts (uses WSL tmux on Windows, native tmux on Linux/macOS)
50
+ - **SSH integration**: Uses your existing SSH keys and honors OpenSSH aliases
51
+ - **Terminal in browser**: Opens `tmux new -As <session> -c <dir>` on the remote host and bridges it via WebSocket + xterm.js
52
+ - **File management**: HTMX-based file browser with preview, edit, delete, and "Open Terminal Here"
53
+ - **Auto-configuration**: Creates starter config on first run
54
+ - **Alias resolution**: Falls back to `ssh -G` when DNS fails; reset overrides with one click
55
+ - **File operations**: Preview, edit (≤256 KB), and delete files with CodeMirror editor
56
+ - **Bilingual UI**: Full English and Japanese language support
51
57
 
52
- - Runs on your Windows 11 laptop (or any OS with Python)
53
- - **日本語:** Windows 11 を含む Python が動く環境ならどこでも動作します。
54
- - Includes a "local" workspace card so you can browse your own filesystem and launch WSL-backed tmux sessions alongside remote hosts
55
- - **日本語:** ローカルワークスペースカードを備え、ローカル/WSL のファイルや tmux も同じ UI で扱えます。
56
- - Uses your existing SSH keys
57
- - **日本語:** 既存の SSH 鍵をそのまま利用します。
58
- - Opens `tmux new -As <session> -c <dir>` on the remote host and bridges it to the browser via WebSocket + xterm.js
59
- - **日本語:** リモートで `tmux new -As <session> -c <dir>` を実行し、WebSocket と xterm.js でブラウザに橋渡しします。
60
- - HTMX-based file browser with “Open Terminal Here”
61
- - **日本語:** HTMX 製のファイルブラウザから「このディレクトリで端末を開く」がワンクリックで可能です。
62
- - Auto-creates a starter config at first run
63
- - **日本語:** 初回起動時に自動で設定ファイルを生成します。
64
- - Honors your OpenSSH aliases; if DNS fails it resolves them via `ssh -G` and you can reset overrides with a single click
65
- - **日本語:** OpenSSH のエイリアスを解釈し、DNS 解決に失敗した場合でも `ssh -G` で補完します。ワンクリックで上書きをリセットできます。
66
- - One-click file previews: view remote files in a new tab without leaving the browser
67
- - **日本語:** ワンクリックでプレビュー表示。ブラウザを離れずに内容確認できます。
68
- - Inline edits for lightweight text files (≤256 KB) with a CodeMirror editor and Save button
69
- - **日本語:** 256 KB 以下のテキストならブラウザ内で編集し、その場で保存できます。
58
+ ## Install
70
59
 
71
- ## Install / インストール
72
-
73
- ### PyPI (recommended) / PyPI からのインストール
60
+ ### PyPI (recommended)
74
61
 
75
62
  ```bash
76
63
  pip install sshler
77
64
 
78
- # launch once to create the config + systemd/service assets
65
+ # Launch once to create the config + systemd/service assets
79
66
  sshler serve
80
67
  ```
81
68
 
82
- - **日本語:** `pip install sshler` で最新の安定版を取得し、`sshler serve` を実行すると初期設定ファイルが生成されます。
83
-
84
69
  Requires Python **3.12+**.
85
70
 
86
- ### Development / 開発インストール
71
+ ### Development
87
72
 
88
73
  ```bash
89
74
  uv pip install -e .
90
75
  # or: pip install -e .
91
76
  ```
92
77
 
93
- - **日本語:** 開発時は editable install (`-e`) を利用してください。
94
-
95
78
  After cloning the repository, install the dev extras and run the usual tooling:
96
79
 
97
80
  ```bash
@@ -100,9 +83,7 @@ uv run ruff check .
100
83
  uv run pytest
101
84
  ```
102
85
 
103
- - **日本語:** リポジトリを取得したら `uv sync --group dev` で依存関係をそろえ、`uv run ruff check .` や `uv run pytest` で動作を確認します。
104
-
105
- ## Run / 実行
86
+ ## Run
106
87
 
107
88
  ```bash
108
89
  sshler serve
@@ -110,8 +91,6 @@ sshler serve
110
91
 
111
92
  The app will open `http://127.0.0.1:8822` in your default browser.
112
93
 
113
- - **日本語:** 上記を実行するとデフォルトブラウザで `http://127.0.0.1:8822` が開きます。
114
-
115
94
  ## Configuration / 設定
116
95
 
117
96
  sshler reads your existing OpenSSH config (`~/.ssh/config`) and shows every concrete `Host` entry automatically. Any favourites, default directories, or custom hosts you add through the UI are stored in a companion YAML file.
@@ -167,9 +146,9 @@ Hit “Add Box” in the UI to define a host that isn’t in your SSH config (fo
167
146
 
168
147
  ### CLI options
169
148
 
170
- ```
149
+ ```bash
171
150
  sshler serve \
172
- --bind 127.0.0.1 \
151
+ --host 127.0.0.1 \
173
152
  --port 8822 \
174
153
  --max-upload-mb 50 \
175
154
  --allow-origin http://workstation:8822 \
@@ -178,17 +157,28 @@ sshler serve \
178
157
  --log-level info
179
158
  ```
180
159
 
181
- - `--bind` (alias `--host`) keeps the server on localhost by default.
182
- - `--allow-origin` can be repeated to expand CORS; combine it with `--auth` if you expose the UI to the LAN.
183
- - `--max-upload-mb` lets you raise/lower the upload ceiling.
160
+ - `--host` (alias `--bind`) sets the bind address (default: `127.0.0.1` for localhost-only). Use `0.0.0.0` to expose on all interfaces, but **only on trusted networks with `--auth` and TLS**.
161
+ - `--port` sets the port number (default: `8822`).
162
+ - `--allow-origin` can be repeated to expand CORS; combine it with `--auth` if you expose the UI beyond localhost.
163
+ - `--auth user:pass` enables HTTP basic authentication (recommended if binding to `0.0.0.0`).
164
+ - `--max-upload-mb` sets the upload size limit (default: 50 MB).
184
165
  - `--no-ssh-alias` disables the `ssh -G` fallback when DNS fails.
185
166
  - `--token` lets you supply your own `X-SSHLER-TOKEN` (otherwise a secure random value is generated).
186
- - `--log-level` feeds directly into uvicorn.
167
+ - `--log-level` feeds directly into uvicorn (options: `critical`, `error`, `warning`, `info`, `debug`, `trace`).
187
168
 
188
169
  The server prints the token (and, if enabled, the basic auth username) on startup so you can copy it into API clients or browser extensions.
189
170
 
190
171
  - **日本語:** サーバー起動時にトークン(および Basic 認証を有効にした場合はユーザー名)を表示するので、API クライアントやブラウザ拡張に貼り付けて利用できます。
191
172
 
173
+ ### Terminal notifications
174
+
175
+ - Send a bell (`printf '\a'`) from tmux or your shell to flash the browser title and raise a desktop notification whenever the sshler tab is hidden.
176
+ - For richer messages use OSC 777: `printf '\033]777;notify=Codex%20done|Check%20the%20output\a'`. The text before the `|` becomes the title; the second part is the body.
177
+ - JSON payloads are also supported: `printf '\033]777;notify={"title":"Codex","message":"All tasks finished"}\a'`.
178
+ - The first notification prompts the browser for permission. Denying it still leaves the in-app toast and title badge when you return to the tab.
179
+
180
+ - **日本語:** タブが非表示のときに `printf '\a'` でベルを送ると、ブラウザタイトルが点滅しデスクトップ通知が表示されます。`printf '\033]777;notify=タイトル|本文\a'` で任意メッセージを送信でき、JSON (`notify={"title":"...","message":"..."}`) にも対応しています。初回は通知許可のダイアログが表示されますが、拒否した場合でもタブ復帰時にトーストとタイトルのバッジで気付けます。
181
+
192
182
  ## Autostart / 自動起動
193
183
 
194
184
  ### Windows (Task Scheduler)
@@ -238,8 +228,92 @@ All assets are used under their respective MIT/BSD-style licenses. sshler itself
238
228
 
239
229
  - **日本語:** 依存ライブラリはいずれも寛容なライセンス (MIT/BSD) で提供されています。sshler 本体も MIT ライセンスで配布されます。
240
230
 
241
- ## Why sshler”? / 名前の由来
231
+ ## Why "sshler"?
232
+
233
+ Because sometimes you want less VS Code, more terminal — but still in a nice browser tab.
234
+
235
+ ---
236
+
237
+ # 日本語ドキュメント
238
+
239
+ sshler はローカル専用の軽量 Web UI で、リモートファイルを SFTP で閲覧したり、ブラウザ上で tmux セッションに接続したりできます。リモート側に追加ソフトをインストールする必要はありません。
240
+
241
+ ## 特徴
242
+
243
+ - **クロスプラットフォーム**: Windows 11、macOS、Linux で動作(Python 3.12+ が必要)
244
+ - **ローカルワークスペース**: ローカルファイルシステムを閲覧し、リモートホストと並べてネイティブの tmux セッションを起動(Windows では WSL tmux、Linux/macOS ではネイティブ tmux を使用)
245
+ - **SSH 統合**: 既存の SSH 鍵を使用し、OpenSSH エイリアスに対応
246
+ - **ブラウザ内ターミナル**: リモートホストで `tmux new -As <session> -c <dir>` を開き、WebSocket + xterm.js 経由で接続
247
+ - **ファイル管理**: プレビュー、編集、削除、「ここでターミナルを開く」機能を備えた HTMX ベースのファイルブラウザ
248
+ - **自動設定**: 初回起動時にスターター設定を作成
249
+ - **エイリアス解決**: DNS 失敗時は `ssh -G` にフォールバック。ワンクリックで上書きをリセット
250
+ - **ファイル操作**: CodeMirror エディタでファイルのプレビュー、編集(256 KB 以下)、削除が可能
251
+ - **バイリンガル UI**: 英語と日本語の完全サポート
252
+
253
+ ## インストール
254
+
255
+ ### PyPI(推奨)
256
+
257
+ ```bash
258
+ pip install sshler
259
+
260
+ # 設定ファイルと systemd/サービスアセットを作成するため一度起動
261
+ sshler serve
262
+ ```
263
+
264
+ Python **3.12+** が必要です。
265
+
266
+ ### 開発用
267
+
268
+ ```bash
269
+ uv pip install -e .
270
+ # または: pip install -e .
271
+ ```
272
+
273
+ リポジトリをクローンした後、dev extras をインストールして通常のツールを実行:
274
+
275
+ ```bash
276
+ uv sync --group dev
277
+ uv run ruff check .
278
+ uv run pytest
279
+ ```
280
+
281
+ ## 実行
282
+
283
+ ```bash
284
+ sshler serve
285
+ ```
286
+
287
+ デフォルトブラウザで `http://127.0.0.1:8822` が開きます。
288
+
289
+ ## 設定
290
+
291
+ sshler は既存の OpenSSH 設定(`~/.ssh/config`)を読み取り、すべての具体的な `Host` エントリを自動的に表示します。UI を通じて追加したお気に入り、デフォルトディレクトリ、カスタムホストは、付属の YAML ファイルに保存されます。
292
+
293
+ 設定ファイルは初回実行時に作成されます:
294
+
295
+ - Windows: `%APPDATA%\sshler\boxes.yaml`
296
+ - macOS/Linux: `~/.config/sshler/boxes.yaml`
297
+
298
+ 例:
299
+
300
+ ```yaml
301
+ boxes:
302
+ - name: gabu-server
303
+ host: example.tailnet.ts.net
304
+ ssh_alias: gabu-server
305
+ user: gabu
306
+ port: 22
307
+ keyfile: "C:/Users/gabu/.ssh/id_ed25519"
308
+ favorites:
309
+ - /home/gabu
310
+ - /home/gabu/projects
311
+ - /srv/codex
312
+ default_dir: /home/gabu
313
+ ```
314
+
315
+ > ヒント: ホームパスが `/home/<user>` でない場合は `default_dir` を設定してください。OpenSSH エイリアスを使用する場合は `ssh_alias:` を追加すると、DNS 失敗時に `ssh -G` で解決します。
242
316
 
243
- **English:** Because sometimes you want less VS Code, more terminal — but still in a nice browser tab.
317
+ ## 名前の由来
244
318
 
245
- **日本語:** VS Code だけに頼らず、ブラウザタブの中で軽快にターミナルを扱いたい──そんな願いからこの名前になりました。
319
+ VS Code だけに頼らず、ブラウザタブの中で軽快にターミナルを扱いたい──そんな願いからこの名前になりました。
@@ -1,57 +1,39 @@
1
+ # sshler
1
2
 
2
- # sshler / エスエスエイチラー
3
-
4
- **English:** sshler is a lightweight, local-only web UI that lets you browse remote
5
- files over SFTP and jump into tmux sessions in your browser — without installing
6
- anything on the remote host.
7
-
8
- **日本語:** sshler はローカル専用の軽量 Web UI で、リモートファイルを SFTP で閲覧したり、
9
- ブラウザ上で tmux セッションに接続したりできます。リモート側に追加ソフトを
10
- インストールする必要はありません。
11
-
12
- - Runs on your Windows 11 laptop (or any OS with Python)
13
- - **日本語:** Windows 11 を含む Python が動く環境ならどこでも動作します。
14
- - Includes a "local" workspace card so you can browse your own filesystem and launch WSL-backed tmux sessions alongside remote hosts
15
- - **日本語:** ローカルワークスペースカードを備え、ローカル/WSL のファイルや tmux も同じ UI で扱えます。
16
- - Uses your existing SSH keys
17
- - **日本語:** 既存の SSH 鍵をそのまま利用します。
18
- - Opens `tmux new -As <session> -c <dir>` on the remote host and bridges it to the browser via WebSocket + xterm.js
19
- - **日本語:** リモートで `tmux new -As <session> -c <dir>` を実行し、WebSocket と xterm.js でブラウザに橋渡しします。
20
- - HTMX-based file browser with “Open Terminal Here”
21
- - **日本語:** HTMX 製のファイルブラウザから「このディレクトリで端末を開く」がワンクリックで可能です。
22
- - Auto-creates a starter config at first run
23
- - **日本語:** 初回起動時に自動で設定ファイルを生成します。
24
- - Honors your OpenSSH aliases; if DNS fails it resolves them via `ssh -G` and you can reset overrides with a single click
25
- - **日本語:** OpenSSH のエイリアスを解釈し、DNS 解決に失敗した場合でも `ssh -G` で補完します。ワンクリックで上書きをリセットできます。
26
- - One-click file previews: view remote files in a new tab without leaving the browser
27
- - **日本語:** ワンクリックでプレビュー表示。ブラウザを離れずに内容確認できます。
28
- - Inline edits for lightweight text files (≤256 KB) with a CodeMirror editor and Save button
29
- - **日本語:** 256 KB 以下のテキストならブラウザ内で編集し、その場で保存できます。
30
-
31
- ## Install / インストール
32
-
33
- ### PyPI (recommended) / PyPI からのインストール
3
+ sshler is a lightweight, local-only web UI that lets you browse remote files over SFTP and jump into tmux sessions in your browser — without installing anything on the remote host.
4
+
5
+ ## Features
6
+
7
+ - **Cross-platform**: Runs on Windows 11, macOS, and Linux (anywhere with Python 3.12+)
8
+ - **Local workspace**: Browse your own filesystem and launch native tmux sessions alongside remote hosts (uses WSL tmux on Windows, native tmux on Linux/macOS)
9
+ - **SSH integration**: Uses your existing SSH keys and honors OpenSSH aliases
10
+ - **Terminal in browser**: Opens `tmux new -As <session> -c <dir>` on the remote host and bridges it via WebSocket + xterm.js
11
+ - **File management**: HTMX-based file browser with preview, edit, delete, and "Open Terminal Here"
12
+ - **Auto-configuration**: Creates starter config on first run
13
+ - **Alias resolution**: Falls back to `ssh -G` when DNS fails; reset overrides with one click
14
+ - **File operations**: Preview, edit (≤256 KB), and delete files with CodeMirror editor
15
+ - **Bilingual UI**: Full English and Japanese language support
16
+
17
+ ## Install
18
+
19
+ ### PyPI (recommended)
34
20
 
35
21
  ```bash
36
22
  pip install sshler
37
23
 
38
- # launch once to create the config + systemd/service assets
24
+ # Launch once to create the config + systemd/service assets
39
25
  sshler serve
40
26
  ```
41
27
 
42
- - **日本語:** `pip install sshler` で最新の安定版を取得し、`sshler serve` を実行すると初期設定ファイルが生成されます。
43
-
44
28
  Requires Python **3.12+**.
45
29
 
46
- ### Development / 開発インストール
30
+ ### Development
47
31
 
48
32
  ```bash
49
33
  uv pip install -e .
50
34
  # or: pip install -e .
51
35
  ```
52
36
 
53
- - **日本語:** 開発時は editable install (`-e`) を利用してください。
54
-
55
37
  After cloning the repository, install the dev extras and run the usual tooling:
56
38
 
57
39
  ```bash
@@ -60,9 +42,7 @@ uv run ruff check .
60
42
  uv run pytest
61
43
  ```
62
44
 
63
- - **日本語:** リポジトリを取得したら `uv sync --group dev` で依存関係をそろえ、`uv run ruff check .` や `uv run pytest` で動作を確認します。
64
-
65
- ## Run / 実行
45
+ ## Run
66
46
 
67
47
  ```bash
68
48
  sshler serve
@@ -70,8 +50,6 @@ sshler serve
70
50
 
71
51
  The app will open `http://127.0.0.1:8822` in your default browser.
72
52
 
73
- - **日本語:** 上記を実行するとデフォルトブラウザで `http://127.0.0.1:8822` が開きます。
74
-
75
53
  ## Configuration / 設定
76
54
 
77
55
  sshler reads your existing OpenSSH config (`~/.ssh/config`) and shows every concrete `Host` entry automatically. Any favourites, default directories, or custom hosts you add through the UI are stored in a companion YAML file.
@@ -127,9 +105,9 @@ Hit “Add Box” in the UI to define a host that isn’t in your SSH config (fo
127
105
 
128
106
  ### CLI options
129
107
 
130
- ```
108
+ ```bash
131
109
  sshler serve \
132
- --bind 127.0.0.1 \
110
+ --host 127.0.0.1 \
133
111
  --port 8822 \
134
112
  --max-upload-mb 50 \
135
113
  --allow-origin http://workstation:8822 \
@@ -138,17 +116,28 @@ sshler serve \
138
116
  --log-level info
139
117
  ```
140
118
 
141
- - `--bind` (alias `--host`) keeps the server on localhost by default.
142
- - `--allow-origin` can be repeated to expand CORS; combine it with `--auth` if you expose the UI to the LAN.
143
- - `--max-upload-mb` lets you raise/lower the upload ceiling.
119
+ - `--host` (alias `--bind`) sets the bind address (default: `127.0.0.1` for localhost-only). Use `0.0.0.0` to expose on all interfaces, but **only on trusted networks with `--auth` and TLS**.
120
+ - `--port` sets the port number (default: `8822`).
121
+ - `--allow-origin` can be repeated to expand CORS; combine it with `--auth` if you expose the UI beyond localhost.
122
+ - `--auth user:pass` enables HTTP basic authentication (recommended if binding to `0.0.0.0`).
123
+ - `--max-upload-mb` sets the upload size limit (default: 50 MB).
144
124
  - `--no-ssh-alias` disables the `ssh -G` fallback when DNS fails.
145
125
  - `--token` lets you supply your own `X-SSHLER-TOKEN` (otherwise a secure random value is generated).
146
- - `--log-level` feeds directly into uvicorn.
126
+ - `--log-level` feeds directly into uvicorn (options: `critical`, `error`, `warning`, `info`, `debug`, `trace`).
147
127
 
148
128
  The server prints the token (and, if enabled, the basic auth username) on startup so you can copy it into API clients or browser extensions.
149
129
 
150
130
  - **日本語:** サーバー起動時にトークン(および Basic 認証を有効にした場合はユーザー名)を表示するので、API クライアントやブラウザ拡張に貼り付けて利用できます。
151
131
 
132
+ ### Terminal notifications
133
+
134
+ - Send a bell (`printf '\a'`) from tmux or your shell to flash the browser title and raise a desktop notification whenever the sshler tab is hidden.
135
+ - For richer messages use OSC 777: `printf '\033]777;notify=Codex%20done|Check%20the%20output\a'`. The text before the `|` becomes the title; the second part is the body.
136
+ - JSON payloads are also supported: `printf '\033]777;notify={"title":"Codex","message":"All tasks finished"}\a'`.
137
+ - The first notification prompts the browser for permission. Denying it still leaves the in-app toast and title badge when you return to the tab.
138
+
139
+ - **日本語:** タブが非表示のときに `printf '\a'` でベルを送ると、ブラウザタイトルが点滅しデスクトップ通知が表示されます。`printf '\033]777;notify=タイトル|本文\a'` で任意メッセージを送信でき、JSON (`notify={"title":"...","message":"..."}`) にも対応しています。初回は通知許可のダイアログが表示されますが、拒否した場合でもタブ復帰時にトーストとタイトルのバッジで気付けます。
140
+
152
141
  ## Autostart / 自動起動
153
142
 
154
143
  ### Windows (Task Scheduler)
@@ -198,8 +187,92 @@ All assets are used under their respective MIT/BSD-style licenses. sshler itself
198
187
 
199
188
  - **日本語:** 依存ライブラリはいずれも寛容なライセンス (MIT/BSD) で提供されています。sshler 本体も MIT ライセンスで配布されます。
200
189
 
201
- ## Why sshler”? / 名前の由来
190
+ ## Why "sshler"?
191
+
192
+ Because sometimes you want less VS Code, more terminal — but still in a nice browser tab.
193
+
194
+ ---
195
+
196
+ # 日本語ドキュメント
197
+
198
+ sshler はローカル専用の軽量 Web UI で、リモートファイルを SFTP で閲覧したり、ブラウザ上で tmux セッションに接続したりできます。リモート側に追加ソフトをインストールする必要はありません。
199
+
200
+ ## 特徴
201
+
202
+ - **クロスプラットフォーム**: Windows 11、macOS、Linux で動作(Python 3.12+ が必要)
203
+ - **ローカルワークスペース**: ローカルファイルシステムを閲覧し、リモートホストと並べてネイティブの tmux セッションを起動(Windows では WSL tmux、Linux/macOS ではネイティブ tmux を使用)
204
+ - **SSH 統合**: 既存の SSH 鍵を使用し、OpenSSH エイリアスに対応
205
+ - **ブラウザ内ターミナル**: リモートホストで `tmux new -As <session> -c <dir>` を開き、WebSocket + xterm.js 経由で接続
206
+ - **ファイル管理**: プレビュー、編集、削除、「ここでターミナルを開く」機能を備えた HTMX ベースのファイルブラウザ
207
+ - **自動設定**: 初回起動時にスターター設定を作成
208
+ - **エイリアス解決**: DNS 失敗時は `ssh -G` にフォールバック。ワンクリックで上書きをリセット
209
+ - **ファイル操作**: CodeMirror エディタでファイルのプレビュー、編集(256 KB 以下)、削除が可能
210
+ - **バイリンガル UI**: 英語と日本語の完全サポート
211
+
212
+ ## インストール
213
+
214
+ ### PyPI(推奨)
215
+
216
+ ```bash
217
+ pip install sshler
218
+
219
+ # 設定ファイルと systemd/サービスアセットを作成するため一度起動
220
+ sshler serve
221
+ ```
222
+
223
+ Python **3.12+** が必要です。
224
+
225
+ ### 開発用
226
+
227
+ ```bash
228
+ uv pip install -e .
229
+ # または: pip install -e .
230
+ ```
231
+
232
+ リポジトリをクローンした後、dev extras をインストールして通常のツールを実行:
233
+
234
+ ```bash
235
+ uv sync --group dev
236
+ uv run ruff check .
237
+ uv run pytest
238
+ ```
239
+
240
+ ## 実行
241
+
242
+ ```bash
243
+ sshler serve
244
+ ```
245
+
246
+ デフォルトブラウザで `http://127.0.0.1:8822` が開きます。
247
+
248
+ ## 設定
249
+
250
+ sshler は既存の OpenSSH 設定(`~/.ssh/config`)を読み取り、すべての具体的な `Host` エントリを自動的に表示します。UI を通じて追加したお気に入り、デフォルトディレクトリ、カスタムホストは、付属の YAML ファイルに保存されます。
251
+
252
+ 設定ファイルは初回実行時に作成されます:
253
+
254
+ - Windows: `%APPDATA%\sshler\boxes.yaml`
255
+ - macOS/Linux: `~/.config/sshler/boxes.yaml`
256
+
257
+ 例:
258
+
259
+ ```yaml
260
+ boxes:
261
+ - name: gabu-server
262
+ host: example.tailnet.ts.net
263
+ ssh_alias: gabu-server
264
+ user: gabu
265
+ port: 22
266
+ keyfile: "C:/Users/gabu/.ssh/id_ed25519"
267
+ favorites:
268
+ - /home/gabu
269
+ - /home/gabu/projects
270
+ - /srv/codex
271
+ default_dir: /home/gabu
272
+ ```
273
+
274
+ > ヒント: ホームパスが `/home/<user>` でない場合は `default_dir` を設定してください。OpenSSH エイリアスを使用する場合は `ssh_alias:` を追加すると、DNS 失敗時に `ssh -G` で解決します。
202
275
 
203
- **English:** Because sometimes you want less VS Code, more terminal — but still in a nice browser tab.
276
+ ## 名前の由来
204
277
 
205
- **日本語:** VS Code だけに頼らず、ブラウザタブの中で軽快にターミナルを扱いたい──そんな願いからこの名前になりました。
278
+ VS Code だけに頼らず、ブラウザタブの中で軽快にターミナルを扱いたい──そんな願いからこの名前になりました。
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "sshler"
8
- version = "0.3.2"
8
+ version = "0.3.3"
9
9
  description = "A local FastAPI-powered SSH multiplexer for tmux-in-browser — from your laptop only."
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.12"
@@ -20,6 +20,7 @@ dependencies = [
20
20
  "platformdirs>=4.2",
21
21
  "pydantic>=2.7",
22
22
  "python-multipart>=0.0.9",
23
+ "markdown-it-py>=3.0.0",
23
24
  "sqler>=1.2025.9.24",
24
25
  ]
25
26
  classifiers = [
@@ -2,6 +2,7 @@
2
2
  const FAVICONS = {
3
3
  default: "/static/favicon.svg",
4
4
  terminal: "/static/favicon-terminal.svg",
5
+ "terminal-local": "/static/favicon-terminal-local.svg",
5
6
  };
6
7
  const LANG_KEY = "sshler-language";
7
8
 
@@ -10,11 +11,89 @@
10
11
  "nav.boxes": "Boxes",
11
12
  "nav.addBox": "Add Box",
12
13
  "nav.docs": "Docs",
14
+ "boxes.title": "Boxes",
15
+ "boxes.subtitle": "Pick a box to browse and open a terminal. Hosts are imported from your SSH config and any custom boxes you add here.",
16
+ "boxes.localWorkspace": "Local workspace",
17
+ "boxes.fromSSHConfig": "From SSH config",
18
+ "boxes.customBox": "Custom box",
19
+ "boxes.resolvesTo": "resolves to",
20
+ "boxes.favorites": "Favorites",
21
+ "boxes.open": "Open",
22
+ "boxes.terminal": "Terminal",
23
+ "boxes.refresh": "Refresh",
24
+ "boxes.configFile": "Config file:",
25
+ "box.browse": "Browse",
26
+ "box.name": "Name",
27
+ "box.type": "Type",
28
+ "box.size": "Size",
29
+ "box.actions": "Actions",
30
+ "box.preview": "Preview",
31
+ "box.edit": "Edit",
32
+ "box.delete": "Delete",
33
+ "box.createFile": "Create File",
34
+ "box.uploadFile": "Upload File",
35
+ "box.filename": "Filename",
36
+ "box.create": "Create",
37
+ "box.upload": "Upload",
38
+ "term.session": "Session:",
39
+ "term.back": "Back",
40
+ "term.files": "Files",
41
+ "term.scrollMode": "Scroll Mode",
42
+ "term.escape": "Escape",
43
+ "term.ctrlT": "Ctrl+T",
44
+ "term.ctrlC": "Ctrl+C",
45
+ "term.splitH": "Split %",
46
+ "term.splitV": "Split \"",
47
+ "term.newWindow": "New Window",
48
+ "term.renameWindow": "Rename Window",
49
+ "term.killPane": "Kill Pane",
50
+ "term.nextWindow": "Next Window",
51
+ "term.prevWindow": "Prev Window",
52
+ "term.detach": "Detach",
13
53
  },
14
54
  ja: {
15
55
  "nav.boxes": "ボックス",
16
56
  "nav.addBox": "ボックスを追加",
17
57
  "nav.docs": "ドキュメント",
58
+ "boxes.title": "ボックス",
59
+ "boxes.subtitle": "ボックスを選択してファイルブラウザとターミナルを開きます。SSH 設定からホストが自動的にインポートされ、カスタムボックスも追加できます。",
60
+ "boxes.localWorkspace": "ローカルワークスペース",
61
+ "boxes.fromSSHConfig": "SSH 設定から",
62
+ "boxes.customBox": "カスタムボックス",
63
+ "boxes.resolvesTo": "解決先",
64
+ "boxes.favorites": "お気に入り",
65
+ "boxes.open": "開く",
66
+ "boxes.terminal": "ターミナル",
67
+ "boxes.refresh": "更新",
68
+ "boxes.configFile": "設定ファイル:",
69
+ "box.browse": "ブラウズ",
70
+ "box.name": "名前",
71
+ "box.type": "種類",
72
+ "box.size": "サイズ",
73
+ "box.actions": "操作",
74
+ "box.preview": "プレビュー",
75
+ "box.edit": "編集",
76
+ "box.delete": "削除",
77
+ "box.createFile": "ファイル作成",
78
+ "box.uploadFile": "ファイルアップロード",
79
+ "box.filename": "ファイル名",
80
+ "box.create": "作成",
81
+ "box.upload": "アップロード",
82
+ "term.session": "セッション:",
83
+ "term.back": "戻る",
84
+ "term.files": "ファイル",
85
+ "term.scrollMode": "スクロールモード",
86
+ "term.escape": "Escape",
87
+ "term.ctrlT": "Ctrl+T",
88
+ "term.ctrlC": "Ctrl+C",
89
+ "term.splitH": "横分割 %",
90
+ "term.splitV": "縦分割 \"",
91
+ "term.newWindow": "新規ウィンドウ",
92
+ "term.renameWindow": "ウィンドウ名変更",
93
+ "term.killPane": "ペイン終了",
94
+ "term.nextWindow": "次のウィンドウ",
95
+ "term.prevWindow": "前のウィンドウ",
96
+ "term.detach": "デタッチ",
18
97
  },
19
98
  };
20
99
 
@@ -47,7 +126,7 @@
47
126
  if (!faviconLink) {
48
127
  return;
49
128
  }
50
- const target = mode === "terminal" ? FAVICONS.terminal : FAVICONS.default;
129
+ const target = FAVICONS[mode] || FAVICONS.default;
51
130
  if (faviconLink.getAttribute("href") !== target) {
52
131
  faviconLink.setAttribute("href", target);
53
132
  }
@@ -101,6 +180,16 @@
101
180
  el.textContent = text;
102
181
  }
103
182
  });
183
+
184
+ // Also translate placeholders
185
+ const placeholderElements = document.querySelectorAll("[data-i18n-placeholder]");
186
+ placeholderElements.forEach((el) => {
187
+ const key = el.dataset.i18nPlaceholder;
188
+ const text = I18N[lang]?.[key];
189
+ if (text && el.tagName === "INPUT") {
190
+ el.placeholder = text;
191
+ }
192
+ });
104
193
  }
105
194
 
106
195
  function updateLangToggle(lang) {
@@ -192,7 +281,8 @@
192
281
  // Show modal
193
282
  modal.classList.add("visible");
194
283
 
195
- // Set initial language
284
+ // Set initial language to match current site language
285
+ const currentLang = getStoredLang();
196
286
  switchDocsLanguage(currentLang);
197
287
 
198
288
  // Language switcher in modal
@@ -255,6 +345,12 @@
255
345
  showToast(payload.message, status);
256
346
  });
257
347
 
348
+ // Re-translate after HTMX swaps new content
349
+ document.body.addEventListener("htmx:afterSwap", () => {
350
+ const currentLang = getStoredLang();
351
+ translate(currentLang);
352
+ });
353
+
258
354
  // Event delegation for delete buttons
259
355
  document.body.addEventListener("click", (event) => {
260
356
  const deleteBtn = event.target.closest(".delete-file-btn");
@@ -302,4 +398,7 @@
302
398
  window.sshlerShowToast = showToast;
303
399
  window.sshlerSetFavicon = setFavicon;
304
400
  window.sshlerDeleteFile = deleteFile;
401
+ window.sshlerTranslate = function() {
402
+ translate(getStoredLang());
403
+ };
305
404
  })();