ttyd-mux 0.3.0 → 0.4.0

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 (196) hide show
  1. package/README.md +105 -1
  2. package/dist/caddy/client.d.ts +3 -55
  3. package/dist/caddy/client.d.ts.map +1 -1
  4. package/dist/caddy/client.js +0 -73
  5. package/dist/caddy/client.js.map +1 -1
  6. package/dist/caddy/route-builder.d.ts +49 -0
  7. package/dist/caddy/route-builder.d.ts.map +1 -0
  8. package/dist/caddy/route-builder.js +175 -0
  9. package/dist/caddy/route-builder.js.map +1 -0
  10. package/dist/caddy/types.d.ts +27 -0
  11. package/dist/caddy/types.d.ts.map +1 -0
  12. package/dist/caddy/types.js +3 -0
  13. package/dist/caddy/types.js.map +1 -0
  14. package/dist/client/api-client.d.ts +26 -0
  15. package/dist/client/api-client.d.ts.map +1 -0
  16. package/dist/client/api-client.js +62 -0
  17. package/dist/client/api-client.js.map +1 -0
  18. package/dist/client/daemon-client.d.ts +48 -0
  19. package/dist/client/daemon-client.d.ts.map +1 -0
  20. package/dist/client/daemon-client.js +205 -0
  21. package/dist/client/daemon-client.js.map +1 -0
  22. package/dist/client/index.d.ts +2 -10
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/index.js +4 -136
  25. package/dist/client/index.js.map +1 -1
  26. package/dist/commands/attach.js +3 -4
  27. package/dist/commands/attach.js.map +1 -1
  28. package/dist/commands/caddy.d.ts +2 -1
  29. package/dist/commands/caddy.d.ts.map +1 -1
  30. package/dist/commands/caddy.js +227 -75
  31. package/dist/commands/caddy.js.map +1 -1
  32. package/dist/commands/daemon.js.map +1 -1
  33. package/dist/commands/deploy.d.ts +7 -0
  34. package/dist/commands/deploy.d.ts.map +1 -0
  35. package/dist/commands/deploy.js +100 -0
  36. package/dist/commands/deploy.js.map +1 -0
  37. package/dist/commands/doctor.d.ts +8 -0
  38. package/dist/commands/doctor.d.ts.map +1 -0
  39. package/dist/commands/doctor.js +180 -0
  40. package/dist/commands/doctor.js.map +1 -0
  41. package/dist/commands/down.d.ts.map +1 -1
  42. package/dist/commands/down.js +11 -0
  43. package/dist/commands/down.js.map +1 -1
  44. package/dist/commands/reload.d.ts +14 -0
  45. package/dist/commands/reload.d.ts.map +1 -0
  46. package/dist/commands/reload.js +50 -0
  47. package/dist/commands/reload.js.map +1 -0
  48. package/dist/commands/shutdown.d.ts +2 -1
  49. package/dist/commands/shutdown.d.ts.map +1 -1
  50. package/dist/commands/shutdown.js +8 -2
  51. package/dist/commands/shutdown.js.map +1 -1
  52. package/dist/commands/start.d.ts.map +1 -1
  53. package/dist/commands/start.js +16 -3
  54. package/dist/commands/start.js.map +1 -1
  55. package/dist/commands/status.js.map +1 -1
  56. package/dist/commands/stop.js.map +1 -1
  57. package/dist/commands/up.js.map +1 -1
  58. package/dist/config/config.d.ts.map +1 -1
  59. package/dist/config/config.js +9 -2
  60. package/dist/config/config.js.map +1 -1
  61. package/dist/config/index.d.ts +3 -3
  62. package/dist/config/index.d.ts.map +1 -1
  63. package/dist/config/index.js +6 -3
  64. package/dist/config/index.js.map +1 -1
  65. package/dist/config/state-store.d.ts +27 -0
  66. package/dist/config/state-store.d.ts.map +1 -0
  67. package/dist/config/state-store.js +55 -0
  68. package/dist/config/state-store.js.map +1 -0
  69. package/dist/config/state.d.ts +6 -0
  70. package/dist/config/state.d.ts.map +1 -1
  71. package/dist/config/state.js +49 -14
  72. package/dist/config/state.js.map +1 -1
  73. package/dist/config/types.d.ts +35 -0
  74. package/dist/config/types.d.ts.map +1 -1
  75. package/dist/config/types.js +23 -1
  76. package/dist/config/types.js.map +1 -1
  77. package/dist/daemon/api-handler.d.ts +5 -0
  78. package/dist/daemon/api-handler.d.ts.map +1 -0
  79. package/dist/daemon/api-handler.js +97 -0
  80. package/dist/daemon/api-handler.js.map +1 -0
  81. package/dist/daemon/config-manager.d.ts +43 -0
  82. package/dist/daemon/config-manager.d.ts.map +1 -0
  83. package/dist/daemon/config-manager.js +154 -0
  84. package/dist/daemon/config-manager.js.map +1 -0
  85. package/dist/daemon/http-proxy.d.ts +27 -0
  86. package/dist/daemon/http-proxy.d.ts.map +1 -0
  87. package/dist/daemon/http-proxy.js +110 -0
  88. package/dist/daemon/http-proxy.js.map +1 -0
  89. package/dist/daemon/ime-helper.d.ts +1 -1
  90. package/dist/daemon/ime-helper.d.ts.map +1 -1
  91. package/dist/daemon/ime-helper.js +284 -10
  92. package/dist/daemon/ime-helper.js.map +1 -1
  93. package/dist/daemon/index.d.ts.map +1 -1
  94. package/dist/daemon/index.js +134 -29
  95. package/dist/daemon/index.js.map +1 -1
  96. package/dist/daemon/portal-utils.d.ts +20 -0
  97. package/dist/daemon/portal-utils.d.ts.map +1 -0
  98. package/dist/daemon/portal-utils.js +109 -0
  99. package/dist/daemon/portal-utils.js.map +1 -0
  100. package/dist/daemon/portal.d.ts.map +1 -1
  101. package/dist/daemon/portal.js +20 -77
  102. package/dist/daemon/portal.js.map +1 -1
  103. package/dist/daemon/pwa.d.ts +52 -0
  104. package/dist/daemon/pwa.d.ts.map +1 -0
  105. package/dist/daemon/pwa.js +229 -0
  106. package/dist/daemon/pwa.js.map +1 -0
  107. package/dist/daemon/router.d.ts +15 -0
  108. package/dist/daemon/router.d.ts.map +1 -0
  109. package/dist/daemon/router.js +164 -0
  110. package/dist/daemon/router.js.map +1 -0
  111. package/dist/daemon/server.d.ts +15 -3
  112. package/dist/daemon/server.d.ts.map +1 -1
  113. package/dist/daemon/server.js +23 -271
  114. package/dist/daemon/server.js.map +1 -1
  115. package/dist/daemon/session-manager.d.ts +44 -10
  116. package/dist/daemon/session-manager.d.ts.map +1 -1
  117. package/dist/daemon/session-manager.js +125 -49
  118. package/dist/daemon/session-manager.js.map +1 -1
  119. package/dist/daemon/session-resolver.d.ts +1 -1
  120. package/dist/daemon/session-resolver.d.ts.map +1 -1
  121. package/dist/daemon/session-resolver.js.map +1 -1
  122. package/dist/daemon/toolbar/config.d.ts +13 -0
  123. package/dist/daemon/toolbar/config.d.ts.map +1 -0
  124. package/dist/daemon/toolbar/config.js +13 -0
  125. package/dist/daemon/toolbar/config.js.map +1 -0
  126. package/dist/daemon/toolbar/index.d.ts +43 -0
  127. package/dist/daemon/toolbar/index.d.ts.map +1 -0
  128. package/dist/daemon/toolbar/index.js +835 -0
  129. package/dist/daemon/toolbar/index.js.map +1 -0
  130. package/dist/daemon/toolbar/styles.d.ts +5 -0
  131. package/dist/daemon/toolbar/styles.d.ts.map +1 -0
  132. package/dist/daemon/toolbar/styles.js +278 -0
  133. package/dist/daemon/toolbar/styles.js.map +1 -0
  134. package/dist/daemon/toolbar/template.d.ts +6 -0
  135. package/dist/daemon/toolbar/template.d.ts.map +1 -0
  136. package/dist/daemon/toolbar/template.js +45 -0
  137. package/dist/daemon/toolbar/template.js.map +1 -0
  138. package/dist/daemon/ws-proxy.d.ts +17 -0
  139. package/dist/daemon/ws-proxy.d.ts.map +1 -0
  140. package/dist/daemon/ws-proxy.js +95 -0
  141. package/dist/daemon/ws-proxy.js.map +1 -0
  142. package/dist/deploy/caddyfile.d.ts +8 -0
  143. package/dist/deploy/caddyfile.d.ts.map +1 -0
  144. package/dist/deploy/caddyfile.js +62 -0
  145. package/dist/deploy/caddyfile.js.map +1 -0
  146. package/dist/deploy/deploy-script.d.ts +8 -0
  147. package/dist/deploy/deploy-script.d.ts.map +1 -0
  148. package/dist/deploy/deploy-script.js +72 -0
  149. package/dist/deploy/deploy-script.js.map +1 -0
  150. package/dist/deploy/static-portal.d.ts +3 -0
  151. package/dist/deploy/static-portal.d.ts.map +1 -0
  152. package/dist/deploy/static-portal.js +59 -0
  153. package/dist/deploy/static-portal.js.map +1 -0
  154. package/dist/index.js +38 -9
  155. package/dist/index.js.map +1 -1
  156. package/dist/test-setup.d.ts +19 -0
  157. package/dist/test-setup.d.ts.map +1 -0
  158. package/dist/test-setup.js +33 -0
  159. package/dist/test-setup.js.map +1 -0
  160. package/dist/tmux.d.ts +28 -1
  161. package/dist/tmux.d.ts.map +1 -1
  162. package/dist/tmux.js +37 -32
  163. package/dist/tmux.js.map +1 -1
  164. package/dist/ui.d.ts +2 -1
  165. package/dist/ui.d.ts.map +1 -1
  166. package/dist/ui.js +16 -9
  167. package/dist/ui.js.map +1 -1
  168. package/dist/utils/errors.d.ts +4 -0
  169. package/dist/utils/errors.d.ts.map +1 -1
  170. package/dist/utils/errors.js +9 -1
  171. package/dist/utils/errors.js.map +1 -1
  172. package/dist/utils/logger.d.ts +14 -0
  173. package/dist/utils/logger.d.ts.map +1 -0
  174. package/dist/utils/logger.js +53 -0
  175. package/dist/utils/logger.js.map +1 -0
  176. package/dist/utils/process-runner.d.ts +50 -0
  177. package/dist/utils/process-runner.d.ts.map +1 -0
  178. package/dist/utils/process-runner.js +73 -0
  179. package/dist/utils/process-runner.js.map +1 -0
  180. package/dist/utils/socket-client.d.ts +24 -0
  181. package/dist/utils/socket-client.d.ts.map +1 -0
  182. package/dist/utils/socket-client.js +30 -0
  183. package/dist/utils/socket-client.js.map +1 -0
  184. package/dist/utils/tmux-client.d.ts +57 -0
  185. package/dist/utils/tmux-client.d.ts.map +1 -0
  186. package/dist/utils/tmux-client.js +117 -0
  187. package/dist/utils/tmux-client.js.map +1 -0
  188. package/dist/version.d.ts +3 -0
  189. package/dist/version.d.ts.map +1 -0
  190. package/dist/version.js +4 -0
  191. package/dist/version.js.map +1 -0
  192. package/package.json +6 -2
  193. package/dist/daemon/proxy.d.ts +0 -7
  194. package/dist/daemon/proxy.d.ts.map +0 -1
  195. package/dist/daemon/proxy.js +0 -17
  196. package/dist/daemon/proxy.js.map +0 -1
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # ttyd-mux
2
2
 
3
+ [![CI](https://github.com/cuzic/ttyd-mux/actions/workflows/ci.yml/badge.svg)](https://github.com/cuzic/ttyd-mux/actions/workflows/ci.yml)
4
+ ![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/cuzic/cc6203266343ecd145c80ea0e848fb33/raw/ttyd-mux-coverage.json)
5
+
3
6
  A CLI tool for managing multiple ttyd+tmux web terminal sessions.
4
7
 
5
8
  複数の ttyd+tmux Web ターミナルセッションを管理する CLI ツール。
@@ -112,6 +115,28 @@ ttyd-mux daemon -f # Start in foreground (debug) / フォアグラ
112
115
  ttyd-mux shutdown # Stop daemon / デーモン終了
113
116
  ```
114
117
 
118
+ ### Diagnostics / 診断
119
+
120
+ ```bash
121
+ ttyd-mux doctor # Check dependencies and configuration / 依存関係と設定をチェック
122
+ ```
123
+
124
+ The `doctor` command checks:
125
+ - ttyd installation
126
+ - tmux installation
127
+ - Bun version (requires 1.0+)
128
+ - Configuration file validity
129
+ - Daemon status
130
+ - Port availability
131
+
132
+ `doctor` コマンドは以下をチェックします:
133
+ - ttyd のインストール
134
+ - tmux のインストール
135
+ - Bun のバージョン(1.0以上が必要)
136
+ - 設定ファイルの妥当性
137
+ - デーモンの状態
138
+ - ポートの空き状況
139
+
115
140
  ## Configuration / 設定
116
141
 
117
142
  Configuration files are searched in the following order:
@@ -143,10 +168,28 @@ listen_addresses:
143
168
  - "::1"
144
169
  # - "0.0.0.0" # Allow external access / 外部からのアクセスを許可する場合
145
170
 
171
+ # Unix socket listeners (optional, for reverse proxy integration)
172
+ # Unix ソケットリスナー(オプション、リバースプロキシ連携用)
173
+ listen_sockets:
174
+ # - /run/ttyd-mux.sock # Caddy: reverse_proxy unix//run/ttyd-mux.sock
175
+
146
176
  # Auto-attach to tmux on session start (default: true)
147
177
  # セッション起動時に自動でtmuxにアタッチ(デフォルト: true)
148
178
  auto_attach: true
149
179
 
180
+ # Proxy mode: "proxy" (default) or "static"
181
+ # プロキシモード: "proxy"(デフォルト)または "static"
182
+ # - proxy: All traffic goes through ttyd-mux daemon (supports IME helper)
183
+ # - static: Sessions are accessed directly via Caddy (lower latency)
184
+ proxy_mode: proxy
185
+
186
+ # Hostname for Caddy integration (used by caddy/deploy commands)
187
+ # Caddy連携用のホスト名(caddy/deployコマンドで使用)
188
+ hostname: example.com
189
+
190
+ # Caddy Admin API URL
191
+ caddy_admin_api: http://localhost:2019
192
+
150
193
  # Predefined sessions (optional) / 事前定義セッション(オプション)
151
194
  sessions:
152
195
  - name: project-a
@@ -162,6 +205,8 @@ sessions:
162
205
 
163
206
  ## Architecture / アーキテクチャ
164
207
 
208
+ ### Proxy Mode (default) / プロキシモード(デフォルト)
209
+
165
210
  ```
166
211
  ┌─────────────────┐
167
212
  │ ttyd :7601 │
@@ -181,6 +226,43 @@ sessions:
181
226
  - **ttyd-mux daemon**: Portal + reverse proxy to ttyd / ポータル表示 + ttyd へのリバースプロキシ
182
227
  - **ttyd**: Web terminal for each session (runs tmux) / 各セッションの Web ターミナル(tmux を起動)
183
228
 
229
+ ### Static Mode / スタティックモード
230
+
231
+ ```
232
+ ┌──────────────┐ ┌─────────────────┐
233
+ │ Static HTML │ │ ttyd :7601 │
234
+ ┌─────────┐ │ (portal) │ │ project-a │
235
+ │ Caddy │──────┼──────────────┼───┼─────────────────┤
236
+ │ │ │ │ │ ttyd :7602 │
237
+ │ :443 │ │ │ │ project-b │
238
+ └─────────┘ └──────────────┘ └─────────────────┘
239
+ ```
240
+
241
+ - Lower latency (no intermediate proxy) / 低レイテンシ(中間プロキシなし)
242
+ - Static portal (no daemon needed at runtime) / 静的ポータル(実行時デーモン不要)
243
+ - No IME helper support / IME ヘルパー非対応
244
+
245
+ ## Toolbar Features / ツールバー機能
246
+
247
+ In proxy mode, ttyd-mux injects a toolbar for improved input experience:
248
+
249
+ プロキシモードでは、入力体験向上のためツールバーが注入されます:
250
+
251
+ ### Mobile Support / モバイル対応
252
+
253
+ - **IME Input**: Virtual keyboard with Japanese IME support / 日本語 IME 対応の仮想キーボード
254
+ - **Touch Pinch Zoom**: Two-finger pinch to resize terminal font (requires Ctrl/Shift button) / 2本指ピンチでフォントサイズ変更(Ctrl/Shift ボタン押下時)
255
+ - **Double-tap Enter**: Double-tap to send Enter key / ダブルタップで Enter キー送信
256
+ - **Minimize Mode**: Compact toolbar showing only input field / コンパクト表示(入力フィールドのみ)
257
+ - **Onboarding Tips**: First-time usage hints / 初回利用時のヒント表示
258
+
259
+ ### PC Browser Support / PC ブラウザ対応
260
+
261
+ - **Ctrl+Scroll Zoom**: Mouse wheel with Ctrl key to resize terminal font / Ctrl+マウスホイールでフォントサイズ変更
262
+ - **Trackpad Pinch Zoom** (Mac): Two-finger pinch gesture on trackpad / トラックパッドの2本指ピンチでフォントサイズ変更
263
+ - **A-/A+ Buttons**: Click buttons in the toolbar / ツールバーのボタンでサイズ変更
264
+ - **Ctrl+J Toggle**: Keyboard shortcut to show/hide toolbar / Ctrl+J でツールバー表示/非表示
265
+
184
266
  ## Caddy Integration / Caddy との連携
185
267
 
186
268
  ### Using Admin API (Recommended) / Admin API で設定(推奨)
@@ -209,7 +291,29 @@ handle /ttyd-mux/* {
209
291
  }
210
292
  ```
211
293
 
212
- See [docs/caddy-setup.md](docs/caddy-setup.md) for details.
294
+ ### Static Mode / スタティックモード
295
+
296
+ For lower latency, use static mode where Caddy routes directly to ttyd:
297
+
298
+ 低レイテンシのために、Caddy から ttyd に直接ルーティングするスタティックモード:
299
+
300
+ ```yaml
301
+ # config.yaml
302
+ proxy_mode: static
303
+ hostname: example.com
304
+ ```
305
+
306
+ ```bash
307
+ # Generate static portal and Caddyfile snippet
308
+ # 静的ポータルと Caddyfile スニペットを生成
309
+ ttyd-mux deploy
310
+
311
+ # Sync routes after starting/stopping sessions
312
+ # セッション開始/停止後にルートを同期
313
+ ttyd-mux caddy sync
314
+ ```
315
+
316
+ See [docs/caddy-setup.md](docs/caddy-setup.md) for details, including authentication setup (Basic, OAuth, mTLS, Authelia/Authentik) for external access.
213
317
 
214
318
  ## File Structure / ファイル構成
215
319
 
@@ -1,35 +1,11 @@
1
- export interface CaddyConfig {
2
- apps?: {
3
- http?: {
4
- servers?: Record<string, CaddyServer>;
5
- };
6
- };
7
- }
8
- export interface CaddyServer {
9
- listen?: string[];
10
- routes?: CaddyRoute[];
11
- }
12
- export interface CaddyRoute {
13
- match?: CaddyMatch[];
14
- handle?: CaddyHandler[];
15
- }
16
- export interface CaddyMatch {
17
- host?: string[];
18
- path?: string[];
19
- }
20
- export interface CaddyHandler {
21
- handler: string;
22
- upstreams?: Array<{
23
- dial: string;
24
- }>;
25
- body?: string;
26
- }
1
+ import type { CaddyConfig, CaddyRoute, CaddyServer } from './types.js';
2
+ export type { CaddyConfig, CaddyRoute, CaddyServer } from './types.js';
27
3
  export declare const DEFAULT_ADMIN_API = "http://localhost:2019";
28
4
  /**
29
5
  * Client for Caddy Admin API
30
6
  */
31
7
  export declare class CaddyClient {
32
- private adminApi;
8
+ private readonly adminApi;
33
9
  constructor(adminApi?: string);
34
10
  /**
35
11
  * Get current Caddy configuration
@@ -47,34 +23,6 @@ export declare class CaddyClient {
47
23
  * Create a new server
48
24
  */
49
25
  createServer(serverName: string, server: CaddyServer): Promise<void>;
50
- /**
51
- * Find server that handles a specific hostname
52
- */
53
- findServerForHost(servers: Record<string, CaddyServer>, hostname: string): {
54
- name: string;
55
- server: CaddyServer;
56
- } | null;
57
- /**
58
- * Check if a route exists for hostname and path
59
- */
60
- routeExists(server: CaddyServer, hostname: string, basePath: string): boolean;
61
- /**
62
- * Create a reverse proxy route
63
- */
64
- createProxyRoute(hostname: string, basePath: string, upstream: string): CaddyRoute;
65
- /**
66
- * Extract upstream from route handlers
67
- */
68
- private extractUpstream;
69
- /**
70
- * Find ttyd-mux routes in all servers
71
- */
72
- findTtydMuxRoutes(servers: Record<string, CaddyServer>, basePath: string): Array<{
73
- serverName: string;
74
- hosts: string[];
75
- paths: string[];
76
- upstream: string;
77
- }>;
78
26
  }
79
27
  /**
80
28
  * Connect to Caddy Admin API and return client
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/caddy/client.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE;YACL,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;SACvC,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;GAEG;AACH,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ;gBAAR,QAAQ,GAAE,MAA0B;IAExD;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC;IAQvC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAKxD;;OAEG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAajF;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1E;;OAEG;IACH,iBAAiB,CACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,QAAQ,EAAE,MAAM,GACf;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI;IAe/C;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAU7E;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;IAiBlF;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,iBAAiB,CACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CAuBH;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,QAAQ,GAAE,MAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CAS/F"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/caddy/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGvE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEvE,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,QAAQ,GAAE,MAA0B;IAIhD;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC;IAQvC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAKxD;;OAEG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAajF;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAY3E;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,QAAQ,GAAE,MAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CAS/F"}
@@ -52,79 +52,6 @@ export class CaddyClient {
52
52
  throw new Error(`Failed to create server: ${response.status} ${text}`);
53
53
  }
54
54
  }
55
- /**
56
- * Find server that handles a specific hostname
57
- */
58
- findServerForHost(servers, hostname) {
59
- for (const [name, server] of Object.entries(servers)) {
60
- const routes = server.routes ?? [];
61
- for (const route of routes) {
62
- const matches = route.match ?? [];
63
- for (const match of matches) {
64
- if (match.host?.includes(hostname)) {
65
- return { name, server };
66
- }
67
- }
68
- }
69
- }
70
- return null;
71
- }
72
- /**
73
- * Check if a route exists for hostname and path
74
- */
75
- routeExists(server, hostname, basePath) {
76
- const routes = server.routes ?? [];
77
- return routes.some((route) => {
78
- const matches = route.match ?? [];
79
- return matches.some((m) => m.host?.includes(hostname) && m.path?.some((p) => p.startsWith(basePath)));
80
- });
81
- }
82
- /**
83
- * Create a reverse proxy route
84
- */
85
- createProxyRoute(hostname, basePath, upstream) {
86
- return {
87
- match: [
88
- {
89
- host: [hostname],
90
- path: [`${basePath}/*`]
91
- }
92
- ],
93
- handle: [
94
- {
95
- handler: 'reverse_proxy',
96
- upstreams: [{ dial: upstream }]
97
- }
98
- ]
99
- };
100
- }
101
- /**
102
- * Extract upstream from route handlers
103
- */
104
- extractUpstream(route) {
105
- const handlers = route.handle ?? [];
106
- const proxyHandler = handlers.find((h) => h.handler === 'reverse_proxy');
107
- return proxyHandler?.upstreams?.[0]?.dial ?? 'unknown';
108
- }
109
- /**
110
- * Find ttyd-mux routes in all servers
111
- */
112
- findTtydMuxRoutes(servers, basePath) {
113
- const results = [];
114
- for (const [serverName, server] of Object.entries(servers)) {
115
- for (const route of server.routes ?? []) {
116
- for (const match of route.match ?? []) {
117
- const paths = match.path ?? [];
118
- if (paths.some((p) => p.startsWith(basePath))) {
119
- const hosts = match.host ?? ['*'];
120
- const upstream = this.extractUpstream(route);
121
- results.push({ serverName, hosts, paths, upstream });
122
- }
123
- }
124
- }
125
- }
126
- return results;
127
- }
128
55
  }
129
56
  /**
130
57
  * Connect to Caddy Admin API and return client
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/caddy/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/caddy/client.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAEzD;;GAEG;AACH,MAAM,OAAO,WAAW;IACL,QAAQ,CAAS;IAElC,YAAY,WAAmB,iBAAiB;QAC9C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,UAAU,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAgB,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB,CAAC,UAAkB,EAAE,MAAoB;QAC/D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,6BAA6B,UAAU,SAAS,EAAE;YAC7F,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;SAC7B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,MAAmB;QACxD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,6BAA6B,UAAU,EAAE,EAAE;YACtF,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;SAC7B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,WAAmB,iBAAiB;IACvE,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;IACzC,oCAAoC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { CaddyRoute, CaddyServer } from './types.js';
2
+ /**
3
+ * Create a reverse proxy route
4
+ */
5
+ export declare function createProxyRoute(hostname: string, basePath: string, upstream: string): CaddyRoute;
6
+ /**
7
+ * Create a route for a specific session (static mode)
8
+ */
9
+ export declare function createSessionRoute(hostname: string, sessionPath: string, port: number): CaddyRoute;
10
+ /**
11
+ * Create portal route (exact match for base path without trailing content)
12
+ */
13
+ export declare function createPortalRoute(hostname: string, basePath: string, daemonPort: number): CaddyRoute;
14
+ /**
15
+ * Find server that handles a specific hostname
16
+ */
17
+ export declare function findServerForHost(servers: Record<string, CaddyServer>, hostname: string): {
18
+ name: string;
19
+ server: CaddyServer;
20
+ } | null;
21
+ /**
22
+ * Check if a route exists for hostname and path
23
+ */
24
+ export declare function routeExists(server: CaddyServer, hostname: string, basePath: string): boolean;
25
+ /**
26
+ * Check if a session route exists
27
+ */
28
+ export declare function sessionRouteExists(server: CaddyServer, hostname: string, sessionPath: string): boolean;
29
+ /**
30
+ * Get all ttyd-mux session routes from a server
31
+ */
32
+ export declare function getSessionRoutes(server: CaddyServer, hostname: string, basePath: string): Array<{
33
+ path: string;
34
+ port: number;
35
+ }>;
36
+ /**
37
+ * Remove session routes that are no longer active
38
+ */
39
+ export declare function filterOutSessionRoutes(routes: CaddyRoute[], hostname: string, basePath: string, keepPaths: Set<string>): CaddyRoute[];
40
+ /**
41
+ * Find ttyd-mux routes in all servers
42
+ */
43
+ export declare function findTtydMuxRoutes(servers: Record<string, CaddyServer>, basePath: string): Array<{
44
+ serverName: string;
45
+ hosts: string[];
46
+ paths: string[];
47
+ upstream: string;
48
+ }>;
49
+ //# sourceMappingURL=route-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-builder.d.ts","sourceRoot":"","sources":["../../src/caddy/route-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAKtE;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAejG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,UAAU,CAeZ;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,UAAU,CAeZ;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CAW9C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAM5F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAMT;AAoDD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAMvC;AA6BD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,UAAU,EAAE,CAEd;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC;IACP,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAsBD"}
@@ -0,0 +1,175 @@
1
+ /** Regex to extract port from upstream URL */
2
+ const PORT_REGEX = /:(\d+)$/;
3
+ /**
4
+ * Create a reverse proxy route
5
+ */
6
+ export function createProxyRoute(hostname, basePath, upstream) {
7
+ return {
8
+ match: [
9
+ {
10
+ host: [hostname],
11
+ path: [`${basePath}/*`]
12
+ }
13
+ ],
14
+ handle: [
15
+ {
16
+ handler: 'reverse_proxy',
17
+ upstreams: [{ dial: upstream }]
18
+ }
19
+ ]
20
+ };
21
+ }
22
+ /**
23
+ * Create a route for a specific session (static mode)
24
+ */
25
+ export function createSessionRoute(hostname, sessionPath, port) {
26
+ return {
27
+ match: [
28
+ {
29
+ host: [hostname],
30
+ path: [`${sessionPath}/*`]
31
+ }
32
+ ],
33
+ handle: [
34
+ {
35
+ handler: 'reverse_proxy',
36
+ upstreams: [{ dial: `localhost:${port}` }]
37
+ }
38
+ ]
39
+ };
40
+ }
41
+ /**
42
+ * Create portal route (exact match for base path without trailing content)
43
+ */
44
+ export function createPortalRoute(hostname, basePath, daemonPort) {
45
+ return {
46
+ match: [
47
+ {
48
+ host: [hostname],
49
+ path: [`${basePath}`, `${basePath}/`, `${basePath}/api/*`]
50
+ }
51
+ ],
52
+ handle: [
53
+ {
54
+ handler: 'reverse_proxy',
55
+ upstreams: [{ dial: `localhost:${daemonPort}` }]
56
+ }
57
+ ]
58
+ };
59
+ }
60
+ /**
61
+ * Find server that handles a specific hostname
62
+ */
63
+ export function findServerForHost(servers, hostname) {
64
+ for (const [name, server] of Object.entries(servers)) {
65
+ for (const route of server.routes ?? []) {
66
+ for (const match of route.match ?? []) {
67
+ if (match.host?.includes(hostname)) {
68
+ return { name, server };
69
+ }
70
+ }
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+ /**
76
+ * Check if a route exists for hostname and path
77
+ */
78
+ export function routeExists(server, hostname, basePath) {
79
+ return (server.routes ?? []).some((route) => (route.match ?? []).some((m) => m.host?.includes(hostname) && m.path?.some((p) => p.startsWith(basePath))));
80
+ }
81
+ /**
82
+ * Check if a session route exists
83
+ */
84
+ export function sessionRouteExists(server, hostname, sessionPath) {
85
+ return (server.routes ?? []).some((route) => (route.match ?? []).some((m) => m.host?.includes(hostname) && m.path?.some((p) => p === `${sessionPath}/*`)));
86
+ }
87
+ /**
88
+ * Extract upstream from route handlers
89
+ */
90
+ function extractUpstream(route) {
91
+ const proxyHandler = (route.handle ?? []).find((h) => h.handler === 'reverse_proxy');
92
+ return proxyHandler?.upstreams?.[0]?.dial ?? 'unknown';
93
+ }
94
+ /**
95
+ * Check if a path matches session route pattern
96
+ */
97
+ function isSessionPath(path, basePath) {
98
+ return path.startsWith(`${basePath}/`) && path.endsWith('/*');
99
+ }
100
+ /**
101
+ * Extract session info from a matching path and route
102
+ */
103
+ function extractSessionInfo(path, route) {
104
+ const sessionPath = path.slice(0, -2); // Remove /*
105
+ const upstream = extractUpstream(route);
106
+ const portMatch = upstream.match(PORT_REGEX);
107
+ if (!portMatch?.[1]) {
108
+ return null;
109
+ }
110
+ return { path: sessionPath, port: Number.parseInt(portMatch[1], 10) };
111
+ }
112
+ /**
113
+ * Extract session routes from a single route match
114
+ */
115
+ function extractSessionRoutesFromMatch(match, route, hostname, basePath) {
116
+ if (!match.host?.includes(hostname)) {
117
+ return [];
118
+ }
119
+ return (match.path ?? [])
120
+ .filter((p) => isSessionPath(p, basePath))
121
+ .map((path) => extractSessionInfo(path, route))
122
+ .filter((info) => info !== null);
123
+ }
124
+ /**
125
+ * Get all ttyd-mux session routes from a server
126
+ */
127
+ export function getSessionRoutes(server, hostname, basePath) {
128
+ return (server.routes ?? []).flatMap((route) => (route.match ?? []).flatMap((match) => extractSessionRoutesFromMatch(match, route, hostname, basePath)));
129
+ }
130
+ /**
131
+ * Check if a route should be removed (is a stale session route)
132
+ */
133
+ function isStaleSessionRoute(route, hostname, basePath, keepPaths) {
134
+ for (const match of route.match ?? []) {
135
+ if (!match.host?.includes(hostname)) {
136
+ continue;
137
+ }
138
+ for (const path of match.path ?? []) {
139
+ if (!isSessionPath(path, basePath)) {
140
+ continue;
141
+ }
142
+ const sessionPath = path.slice(0, -2);
143
+ if (!keepPaths.has(sessionPath)) {
144
+ return true;
145
+ }
146
+ }
147
+ }
148
+ return false;
149
+ }
150
+ /**
151
+ * Remove session routes that are no longer active
152
+ */
153
+ export function filterOutSessionRoutes(routes, hostname, basePath, keepPaths) {
154
+ return routes.filter((route) => !isStaleSessionRoute(route, hostname, basePath, keepPaths));
155
+ }
156
+ /**
157
+ * Find ttyd-mux routes in all servers
158
+ */
159
+ export function findTtydMuxRoutes(servers, basePath) {
160
+ const results = [];
161
+ for (const [serverName, server] of Object.entries(servers)) {
162
+ for (const route of server.routes ?? []) {
163
+ for (const match of route.match ?? []) {
164
+ const paths = match.path ?? [];
165
+ if (paths.some((p) => p.startsWith(basePath))) {
166
+ const hosts = match.host ?? ['*'];
167
+ const upstream = extractUpstream(route);
168
+ results.push({ serverName, hosts, paths, upstream });
169
+ }
170
+ }
171
+ }
172
+ }
173
+ return results;
174
+ }
175
+ //# sourceMappingURL=route-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-builder.js","sourceRoot":"","sources":["../../src/caddy/route-builder.ts"],"names":[],"mappings":"AAEA,8CAA8C;AAC9C,MAAM,UAAU,GAAG,SAAS,CAAC;AAE7B;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,QAAgB,EAAE,QAAgB;IACnF,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,CAAC,QAAQ,CAAC;gBAChB,IAAI,EAAE,CAAC,GAAG,QAAQ,IAAI,CAAC;aACxB;SACF;QACD,MAAM,EAAE;YACN;gBACE,OAAO,EAAE,eAAe;gBACxB,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;aAChC;SACF;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAgB,EAChB,WAAmB,EACnB,IAAY;IAEZ,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,CAAC,QAAQ,CAAC;gBAChB,IAAI,EAAE,CAAC,GAAG,WAAW,IAAI,CAAC;aAC3B;SACF;QACD,MAAM,EAAE;YACN;gBACE,OAAO,EAAE,eAAe;gBACxB,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,IAAI,EAAE,EAAE,CAAC;aAC3C;SACF;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAgB,EAChB,QAAgB,EAChB,UAAkB;IAElB,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,CAAC,QAAQ,CAAC;gBAChB,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,EAAE,GAAG,QAAQ,GAAG,EAAE,GAAG,QAAQ,QAAQ,CAAC;aAC3D;SACF;QACD,MAAM,EAAE;YACN;gBACE,OAAO,EAAE,eAAe;gBACxB,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,UAAU,EAAE,EAAE,CAAC;aACjD;SACF;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAoC,EACpC,QAAgB;IAEhB,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACxC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;gBACtC,IAAI,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACnC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,MAAmB,EAAE,QAAgB,EAAE,QAAgB;IACjF,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC1C,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CACtB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CACjF,CACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAmB,EACnB,QAAgB,EAChB,WAAmB;IAEnB,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC1C,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CACtB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,WAAW,IAAI,CAAC,CACnF,CACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,KAAiB;IACxC,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC;IACrF,OAAO,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,SAAS,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,IAAY,EAAE,QAAgB;IACnD,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CACzB,IAAY,EACZ,KAAiB;IAEjB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;IACnD,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACxE,CAAC;AAED;;GAEG;AACH,SAAS,6BAA6B,CACpC,KAAiB,EACjB,KAAiB,EACjB,QAAgB,EAChB,QAAgB;IAEhB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;SACtB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;SACzC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;SAC9C,MAAM,CAAC,CAAC,IAAI,EAA0C,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC7E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAmB,EACnB,QAAgB,EAChB,QAAgB;IAEhB,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC7C,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACpC,6BAA6B,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAChE,CACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAC1B,KAAiB,EACjB,QAAgB,EAChB,QAAgB,EAChB,SAAsB;IAEtB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,SAAS;QACX,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;gBACnC,SAAS;YACX,CAAC;YACD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAoB,EACpB,QAAgB,EAChB,QAAgB,EAChB,SAAsB;IAEtB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAC9F,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAoC,EACpC,QAAgB;IAOhB,MAAM,OAAO,GAKR,EAAE,CAAC;IAER,KAAK,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACxC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;gBACtC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;gBAC/B,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;oBAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;oBAClC,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;oBACxC,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,27 @@
1
+ export interface CaddyConfig {
2
+ apps?: {
3
+ http?: {
4
+ servers?: Record<string, CaddyServer>;
5
+ };
6
+ };
7
+ }
8
+ export interface CaddyServer {
9
+ listen?: string[];
10
+ routes?: CaddyRoute[];
11
+ }
12
+ export interface CaddyRoute {
13
+ match?: CaddyMatch[];
14
+ handle?: CaddyHandler[];
15
+ }
16
+ export interface CaddyMatch {
17
+ host?: string[];
18
+ path?: string[];
19
+ }
20
+ export interface CaddyHandler {
21
+ handler: string;
22
+ upstreams?: Array<{
23
+ dial: string;
24
+ }>;
25
+ body?: string;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/caddy/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE;YACL,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;SACvC,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,3 @@
1
+ // Type definitions for Caddy config
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/caddy/types.ts"],"names":[],"mappings":"AAAA,oCAAoC"}
@@ -0,0 +1,26 @@
1
+ import type { Config, SessionResponse, StartSessionRequest, StatusResponse } from '../config/types.js';
2
+ /**
3
+ * Make an API request to the daemon
4
+ */
5
+ export declare function apiRequest<T>(config: Config, method: string, path: string, body?: unknown): Promise<T>;
6
+ /**
7
+ * Get daemon status
8
+ */
9
+ export declare function getStatus(config: Config): Promise<StatusResponse>;
10
+ /**
11
+ * Get all sessions
12
+ */
13
+ export declare function getSessions(config: Config): Promise<SessionResponse[]>;
14
+ /**
15
+ * Start a new session
16
+ */
17
+ export declare function startSession(config: Config, request: StartSessionRequest): Promise<SessionResponse>;
18
+ /**
19
+ * Stop a session
20
+ */
21
+ export declare function stopSession(config: Config, name: string): Promise<void>;
22
+ /**
23
+ * Request daemon shutdown
24
+ */
25
+ export declare function requestShutdown(config: Config): Promise<void>;
26
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/client/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,eAAe,EACf,mBAAmB,EACnB,cAAc,EACf,MAAM,mBAAmB,CAAC;AAa3B;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC,CAgBZ;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAEjE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAEtE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7E;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnE"}