ttyd-mux 0.3.1 → 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 (188) hide show
  1. package/README.md +52 -1
  2. package/dist/caddy/client.d.ts +3 -78
  3. package/dist/caddy/client.d.ts.map +1 -1
  4. package/dist/caddy/client.js +0 -170
  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 -149
  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.map +1 -1
  29. package/dist/commands/caddy.js +73 -108
  30. package/dist/commands/caddy.js.map +1 -1
  31. package/dist/commands/daemon.js.map +1 -1
  32. package/dist/commands/deploy.d.ts.map +1 -1
  33. package/dist/commands/deploy.js +3 -8
  34. package/dist/commands/deploy.js.map +1 -1
  35. package/dist/commands/doctor.d.ts +8 -0
  36. package/dist/commands/doctor.d.ts.map +1 -0
  37. package/dist/commands/doctor.js +180 -0
  38. package/dist/commands/doctor.js.map +1 -0
  39. package/dist/commands/down.d.ts.map +1 -1
  40. package/dist/commands/down.js +11 -0
  41. package/dist/commands/down.js.map +1 -1
  42. package/dist/commands/reload.d.ts +14 -0
  43. package/dist/commands/reload.d.ts.map +1 -0
  44. package/dist/commands/reload.js +50 -0
  45. package/dist/commands/reload.js.map +1 -0
  46. package/dist/commands/shutdown.d.ts +2 -1
  47. package/dist/commands/shutdown.d.ts.map +1 -1
  48. package/dist/commands/shutdown.js +8 -2
  49. package/dist/commands/shutdown.js.map +1 -1
  50. package/dist/commands/start.d.ts.map +1 -1
  51. package/dist/commands/start.js +16 -3
  52. package/dist/commands/start.js.map +1 -1
  53. package/dist/commands/status.js.map +1 -1
  54. package/dist/commands/stop.js.map +1 -1
  55. package/dist/commands/up.js.map +1 -1
  56. package/dist/config/config.d.ts.map +1 -1
  57. package/dist/config/config.js +9 -2
  58. package/dist/config/config.js.map +1 -1
  59. package/dist/config/index.d.ts +3 -3
  60. package/dist/config/index.d.ts.map +1 -1
  61. package/dist/config/index.js +6 -3
  62. package/dist/config/index.js.map +1 -1
  63. package/dist/config/state-store.d.ts +27 -0
  64. package/dist/config/state-store.d.ts.map +1 -0
  65. package/dist/config/state-store.js +55 -0
  66. package/dist/config/state-store.js.map +1 -0
  67. package/dist/config/state.d.ts +6 -0
  68. package/dist/config/state.d.ts.map +1 -1
  69. package/dist/config/state.js +49 -14
  70. package/dist/config/state.js.map +1 -1
  71. package/dist/config/types.d.ts +29 -0
  72. package/dist/config/types.d.ts.map +1 -1
  73. package/dist/config/types.js +20 -1
  74. package/dist/config/types.js.map +1 -1
  75. package/dist/daemon/api-handler.d.ts +5 -0
  76. package/dist/daemon/api-handler.d.ts.map +1 -0
  77. package/dist/daemon/api-handler.js +97 -0
  78. package/dist/daemon/api-handler.js.map +1 -0
  79. package/dist/daemon/config-manager.d.ts +43 -0
  80. package/dist/daemon/config-manager.d.ts.map +1 -0
  81. package/dist/daemon/config-manager.js +154 -0
  82. package/dist/daemon/config-manager.js.map +1 -0
  83. package/dist/daemon/http-proxy.d.ts +27 -0
  84. package/dist/daemon/http-proxy.d.ts.map +1 -0
  85. package/dist/daemon/http-proxy.js +110 -0
  86. package/dist/daemon/http-proxy.js.map +1 -0
  87. package/dist/daemon/ime-helper.d.ts +1 -1
  88. package/dist/daemon/ime-helper.d.ts.map +1 -1
  89. package/dist/daemon/ime-helper.js +24 -53
  90. package/dist/daemon/ime-helper.js.map +1 -1
  91. package/dist/daemon/index.d.ts.map +1 -1
  92. package/dist/daemon/index.js +134 -29
  93. package/dist/daemon/index.js.map +1 -1
  94. package/dist/daemon/portal-utils.d.ts +20 -0
  95. package/dist/daemon/portal-utils.d.ts.map +1 -0
  96. package/dist/daemon/portal-utils.js +109 -0
  97. package/dist/daemon/portal-utils.js.map +1 -0
  98. package/dist/daemon/portal.d.ts.map +1 -1
  99. package/dist/daemon/portal.js +20 -77
  100. package/dist/daemon/portal.js.map +1 -1
  101. package/dist/daemon/pwa.d.ts +52 -0
  102. package/dist/daemon/pwa.d.ts.map +1 -0
  103. package/dist/daemon/pwa.js +229 -0
  104. package/dist/daemon/pwa.js.map +1 -0
  105. package/dist/daemon/router.d.ts +15 -0
  106. package/dist/daemon/router.d.ts.map +1 -0
  107. package/dist/daemon/router.js +164 -0
  108. package/dist/daemon/router.js.map +1 -0
  109. package/dist/daemon/server.d.ts +15 -3
  110. package/dist/daemon/server.d.ts.map +1 -1
  111. package/dist/daemon/server.js +23 -271
  112. package/dist/daemon/server.js.map +1 -1
  113. package/dist/daemon/session-manager.d.ts +44 -10
  114. package/dist/daemon/session-manager.d.ts.map +1 -1
  115. package/dist/daemon/session-manager.js +125 -49
  116. package/dist/daemon/session-manager.js.map +1 -1
  117. package/dist/daemon/session-resolver.d.ts +1 -1
  118. package/dist/daemon/session-resolver.d.ts.map +1 -1
  119. package/dist/daemon/session-resolver.js.map +1 -1
  120. package/dist/daemon/toolbar/config.d.ts +13 -0
  121. package/dist/daemon/toolbar/config.d.ts.map +1 -0
  122. package/dist/daemon/toolbar/config.js +13 -0
  123. package/dist/daemon/toolbar/config.js.map +1 -0
  124. package/dist/daemon/toolbar/index.d.ts +43 -0
  125. package/dist/daemon/toolbar/index.d.ts.map +1 -0
  126. package/dist/daemon/toolbar/index.js +835 -0
  127. package/dist/daemon/toolbar/index.js.map +1 -0
  128. package/dist/daemon/toolbar/styles.d.ts +5 -0
  129. package/dist/daemon/toolbar/styles.d.ts.map +1 -0
  130. package/dist/daemon/toolbar/styles.js +278 -0
  131. package/dist/daemon/toolbar/styles.js.map +1 -0
  132. package/dist/daemon/toolbar/template.d.ts +6 -0
  133. package/dist/daemon/toolbar/template.d.ts.map +1 -0
  134. package/dist/daemon/toolbar/template.js +45 -0
  135. package/dist/daemon/toolbar/template.js.map +1 -0
  136. package/dist/daemon/ws-proxy.d.ts +17 -0
  137. package/dist/daemon/ws-proxy.d.ts.map +1 -0
  138. package/dist/daemon/ws-proxy.js +95 -0
  139. package/dist/daemon/ws-proxy.js.map +1 -0
  140. package/dist/deploy/caddyfile.d.ts.map +1 -1
  141. package/dist/deploy/caddyfile.js.map +1 -1
  142. package/dist/deploy/deploy-script.d.ts.map +1 -1
  143. package/dist/deploy/static-portal.d.ts.map +1 -1
  144. package/dist/deploy/static-portal.js +6 -77
  145. package/dist/deploy/static-portal.js.map +1 -1
  146. package/dist/index.js +16 -3
  147. package/dist/index.js.map +1 -1
  148. package/dist/test-setup.d.ts +19 -0
  149. package/dist/test-setup.d.ts.map +1 -0
  150. package/dist/test-setup.js +33 -0
  151. package/dist/test-setup.js.map +1 -0
  152. package/dist/tmux.d.ts +28 -1
  153. package/dist/tmux.d.ts.map +1 -1
  154. package/dist/tmux.js +37 -32
  155. package/dist/tmux.js.map +1 -1
  156. package/dist/ui.d.ts +2 -1
  157. package/dist/ui.d.ts.map +1 -1
  158. package/dist/ui.js +16 -9
  159. package/dist/ui.js.map +1 -1
  160. package/dist/utils/errors.d.ts +4 -0
  161. package/dist/utils/errors.d.ts.map +1 -1
  162. package/dist/utils/errors.js +9 -1
  163. package/dist/utils/errors.js.map +1 -1
  164. package/dist/utils/logger.d.ts +14 -0
  165. package/dist/utils/logger.d.ts.map +1 -0
  166. package/dist/utils/logger.js +53 -0
  167. package/dist/utils/logger.js.map +1 -0
  168. package/dist/utils/process-runner.d.ts +50 -0
  169. package/dist/utils/process-runner.d.ts.map +1 -0
  170. package/dist/utils/process-runner.js +73 -0
  171. package/dist/utils/process-runner.js.map +1 -0
  172. package/dist/utils/socket-client.d.ts +24 -0
  173. package/dist/utils/socket-client.d.ts.map +1 -0
  174. package/dist/utils/socket-client.js +30 -0
  175. package/dist/utils/socket-client.js.map +1 -0
  176. package/dist/utils/tmux-client.d.ts +57 -0
  177. package/dist/utils/tmux-client.d.ts.map +1 -0
  178. package/dist/utils/tmux-client.js +117 -0
  179. package/dist/utils/tmux-client.js.map +1 -0
  180. package/dist/version.d.ts +3 -0
  181. package/dist/version.d.ts.map +1 -0
  182. package/dist/version.js +4 -0
  183. package/dist/version.js.map +1 -0
  184. package/package.json +6 -2
  185. package/dist/daemon/proxy.d.ts +0 -7
  186. package/dist/daemon/proxy.d.ts.map +0 -1
  187. package/dist/daemon/proxy.js +0 -17
  188. package/dist/daemon/proxy.js.map +0 -1
@@ -1,20 +1,39 @@
1
1
  import { z } from 'zod';
2
2
  // === 設定ファイル (config.yaml) ===
3
+ export const TmuxModeSchema = z.enum(['auto', 'attach', 'new']);
3
4
  export const SessionDefinitionSchema = z.object({
4
5
  name: z.string().min(1),
5
6
  dir: z.string().min(1),
6
7
  path: z.string().startsWith('/'),
7
8
  port_offset: z.number().int().min(0)
8
9
  });
10
+ export const ToolbarConfigSchema = z.object({
11
+ font_size_default_mobile: z.number().int().min(8).max(72).default(32),
12
+ font_size_default_pc: z.number().int().min(8).max(72).default(14),
13
+ font_size_min: z.number().int().min(6).max(20).default(10),
14
+ font_size_max: z.number().int().min(24).max(96).default(48),
15
+ double_tap_delay: z.number().int().min(100).max(1000).default(300)
16
+ });
17
+ /** Default toolbar configuration */
18
+ export const DEFAULT_TOOLBAR_CONFIG = {
19
+ font_size_default_mobile: 32,
20
+ font_size_default_pc: 14,
21
+ font_size_min: 10,
22
+ font_size_max: 48,
23
+ double_tap_delay: 300
24
+ };
9
25
  export const ConfigSchema = z.object({
10
26
  base_path: z.string().startsWith('/').default('/ttyd-mux'),
11
27
  base_port: z.number().int().min(1024).max(65535).default(7600),
12
28
  daemon_port: z.number().int().min(1024).max(65535).default(7680),
13
29
  listen_addresses: z.array(z.string()).default(['127.0.0.1', '::1']),
30
+ listen_sockets: z.array(z.string()).default([]),
14
31
  auto_attach: z.boolean().default(true),
15
32
  sessions: z.array(SessionDefinitionSchema).default([]),
16
33
  proxy_mode: z.enum(['proxy', 'static']).default('proxy'),
17
34
  hostname: z.string().optional(),
18
- caddy_admin_api: z.string().default('http://localhost:2019')
35
+ caddy_admin_api: z.string().default('http://localhost:2019'),
36
+ tmux_mode: TmuxModeSchema.default('auto'),
37
+ toolbar: ToolbarConfigSchema.default(DEFAULT_TOOLBAR_CONFIG)
19
38
  });
20
39
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,+BAA+B;AAE/B,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACrC,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;IAC1D,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAChE,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACnE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACtC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACtD,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACxD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,uBAAuB,CAAC;CAC7D,CAAC,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { Config } from '../config/types.js';
3
+ export declare function sendJson(res: ServerResponse, status: number, data: unknown): void;
4
+ export declare function handleApiRequest(config: Config, req: IncomingMessage, res: ServerResponse): void;
5
+ //# sourceMappingURL=api-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../src/daemon/api-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAchD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAOjF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAyFhG"}
@@ -0,0 +1,97 @@
1
+ import { getFullPath, normalizeBasePath } from '../config/config.js';
2
+ import { getDaemonState } from '../config/state.js';
3
+ import { getErrorMessage } from '../utils/errors.js';
4
+ import { isValidSessionName, sanitizeSessionName } from '../utils/tmux-client.js';
5
+ import { generateJsonResponse } from './portal.js';
6
+ import { allocatePort, sessionManager, sessionNameFromDir } from './session-manager.js';
7
+ /** Regex to match DELETE /api/sessions/:name */
8
+ const DELETE_SESSION_REGEX = /^\/api\/sessions\/(.+)$/;
9
+ export function sendJson(res, status, data) {
10
+ const body = generateJsonResponse(data);
11
+ res.writeHead(status, {
12
+ 'Content-Type': 'application/json',
13
+ 'Content-Length': Buffer.byteLength(body)
14
+ });
15
+ res.end(body);
16
+ }
17
+ export function handleApiRequest(config, req, res) {
18
+ const basePath = normalizeBasePath(config.base_path);
19
+ const url = req.url ?? '/';
20
+ const path = url.slice(basePath.length);
21
+ const method = req.method ?? 'GET';
22
+ // GET /api/status
23
+ if (path === '/api/status' && method === 'GET') {
24
+ const daemon = getDaemonState();
25
+ const sessions = sessionManager.listSessions().map((s) => ({
26
+ ...s,
27
+ fullPath: getFullPath(config, s.path)
28
+ }));
29
+ sendJson(res, 200, { daemon, sessions });
30
+ return;
31
+ }
32
+ // GET /api/sessions
33
+ if (path === '/api/sessions' && method === 'GET') {
34
+ const sessions = sessionManager.listSessions().map((s) => ({
35
+ ...s,
36
+ fullPath: getFullPath(config, s.path)
37
+ }));
38
+ sendJson(res, 200, sessions);
39
+ return;
40
+ }
41
+ // POST /api/sessions
42
+ if (path === '/api/sessions' && method === 'POST') {
43
+ let body = '';
44
+ req.on('data', (chunk) => {
45
+ body += chunk.toString();
46
+ });
47
+ req.on('end', async () => {
48
+ try {
49
+ const parsed = JSON.parse(body);
50
+ const rawName = parsed.name ?? sessionNameFromDir(parsed.dir);
51
+ // Sanitize session name to prevent command injection
52
+ const name = isValidSessionName(rawName) ? rawName : sanitizeSessionName(rawName);
53
+ const sessionPath = parsed.path ?? `/${name}`;
54
+ const port = allocatePort(config);
55
+ const fullPath = getFullPath(config, sessionPath);
56
+ const options = {
57
+ name,
58
+ dir: parsed.dir,
59
+ path: sessionPath,
60
+ port,
61
+ fullPath,
62
+ tmuxMode: config.tmux_mode
63
+ };
64
+ const session = await sessionManager.startSession(options);
65
+ sendJson(res, 201, { ...session, fullPath });
66
+ }
67
+ catch (error) {
68
+ sendJson(res, 400, { error: getErrorMessage(error) });
69
+ }
70
+ });
71
+ return;
72
+ }
73
+ // DELETE /api/sessions/:name
74
+ const deleteMatch = path.match(DELETE_SESSION_REGEX);
75
+ if (deleteMatch?.[1] && method === 'DELETE') {
76
+ const name = decodeURIComponent(deleteMatch[1]);
77
+ try {
78
+ sessionManager.stopSession(name);
79
+ sendJson(res, 200, { success: true });
80
+ }
81
+ catch (error) {
82
+ sendJson(res, 400, { error: getErrorMessage(error) });
83
+ }
84
+ return;
85
+ }
86
+ // POST /api/shutdown
87
+ if (path === '/api/shutdown' && method === 'POST') {
88
+ sendJson(res, 200, { success: true });
89
+ setTimeout(() => {
90
+ process.exit(0);
91
+ }, 100);
92
+ return;
93
+ }
94
+ // Not found
95
+ sendJson(res, 404, { error: 'API endpoint not found' });
96
+ }
97
+ //# sourceMappingURL=api-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-handler.js","sourceRoot":"","sources":["../../src/daemon/api-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Daemon Configuration Manager
3
+ *
4
+ * Manages the daemon's configuration with support for hot-reload.
5
+ */
6
+ import type { Config } from '../config/types.js';
7
+ export interface ReloadResult {
8
+ success: boolean;
9
+ reloaded: string[];
10
+ requiresRestart: string[];
11
+ error?: string;
12
+ }
13
+ declare class ConfigManager {
14
+ private config;
15
+ private configPath?;
16
+ constructor(configPath?: string);
17
+ /**
18
+ * Get the current configuration
19
+ */
20
+ getConfig(): Config;
21
+ /**
22
+ * Reload configuration from disk
23
+ */
24
+ reload(): ReloadResult;
25
+ }
26
+ /**
27
+ * Initialize the config manager
28
+ */
29
+ export declare function initConfigManager(configPath?: string): ConfigManager;
30
+ /**
31
+ * Get the config manager instance
32
+ */
33
+ export declare function getConfigManager(): ConfigManager;
34
+ /**
35
+ * Get the current config (convenience function)
36
+ */
37
+ export declare function getCurrentConfig(): Config;
38
+ /**
39
+ * Reload the config (convenience function)
40
+ */
41
+ export declare function reloadConfig(): ReloadResult;
42
+ export {};
43
+ //# sourceMappingURL=config-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../src/daemon/config-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAMhD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0ED,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAC,CAAS;gBAEhB,UAAU,CAAC,EAAE,MAAM;IAK/B;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,MAAM,IAAI,YAAY;CAyCvB;AAKD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CAGpE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAKhD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,YAAY,CAE3C"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Daemon Configuration Manager
3
+ *
4
+ * Manages the daemon's configuration with support for hot-reload.
5
+ */
6
+ import { loadConfig } from '../config/config.js';
7
+ import { createLogger } from '../utils/logger.js';
8
+ const log = createLogger('config-manager');
9
+ /**
10
+ * Compare two configs and identify what changed
11
+ */
12
+ function detectChanges(oldConfig, newConfig) {
13
+ const hotReloadable = [];
14
+ const requiresRestart = [];
15
+ // Check toolbar config
16
+ const toolbarKeys = [
17
+ 'font_size_default_mobile',
18
+ 'font_size_default_pc',
19
+ 'font_size_min',
20
+ 'font_size_max',
21
+ 'double_tap_delay'
22
+ ];
23
+ for (const key of toolbarKeys) {
24
+ if (oldConfig.toolbar[key] !== newConfig.toolbar[key]) {
25
+ hotReloadable.push(`toolbar.${key}`);
26
+ }
27
+ }
28
+ // Check session definitions
29
+ const oldSessionNames = new Set(oldConfig.sessions?.map((s) => s.name) ?? []);
30
+ const newSessionNames = new Set(newConfig.sessions?.map((s) => s.name) ?? []);
31
+ if (oldSessionNames.size !== newSessionNames.size ||
32
+ ![...oldSessionNames].every((n) => newSessionNames.has(n))) {
33
+ hotReloadable.push('sessions');
34
+ }
35
+ // Check other hot-reloadable settings
36
+ if (oldConfig.proxy_mode !== newConfig.proxy_mode) {
37
+ hotReloadable.push('proxy_mode');
38
+ }
39
+ if (oldConfig.hostname !== newConfig.hostname) {
40
+ hotReloadable.push('hostname');
41
+ }
42
+ if (oldConfig.caddy_admin_api !== newConfig.caddy_admin_api) {
43
+ hotReloadable.push('caddy_admin_api');
44
+ }
45
+ if (oldConfig.tmux_mode !== newConfig.tmux_mode) {
46
+ hotReloadable.push('tmux_mode');
47
+ }
48
+ if (oldConfig.auto_attach !== newConfig.auto_attach) {
49
+ hotReloadable.push('auto_attach');
50
+ }
51
+ // Check settings that require restart
52
+ if (oldConfig.daemon_port !== newConfig.daemon_port) {
53
+ requiresRestart.push('daemon_port');
54
+ }
55
+ if (oldConfig.base_path !== newConfig.base_path) {
56
+ requiresRestart.push('base_path');
57
+ }
58
+ if (oldConfig.base_port !== newConfig.base_port) {
59
+ requiresRestart.push('base_port');
60
+ }
61
+ if (JSON.stringify(oldConfig.listen_addresses) !== JSON.stringify(newConfig.listen_addresses)) {
62
+ requiresRestart.push('listen_addresses');
63
+ }
64
+ if (JSON.stringify(oldConfig.listen_sockets) !== JSON.stringify(newConfig.listen_sockets)) {
65
+ requiresRestart.push('listen_sockets');
66
+ }
67
+ return { hotReloadable, requiresRestart };
68
+ }
69
+ class ConfigManager {
70
+ config;
71
+ configPath;
72
+ constructor(configPath) {
73
+ this.configPath = configPath;
74
+ this.config = loadConfig(configPath);
75
+ }
76
+ /**
77
+ * Get the current configuration
78
+ */
79
+ getConfig() {
80
+ return this.config;
81
+ }
82
+ /**
83
+ * Reload configuration from disk
84
+ */
85
+ reload() {
86
+ try {
87
+ const newConfig = loadConfig(this.configPath);
88
+ const { hotReloadable, requiresRestart } = detectChanges(this.config, newConfig);
89
+ if (hotReloadable.length === 0 && requiresRestart.length === 0) {
90
+ log.info('Config reload: no changes detected');
91
+ return {
92
+ success: true,
93
+ reloaded: [],
94
+ requiresRestart: []
95
+ };
96
+ }
97
+ // Apply the new config
98
+ this.config = newConfig;
99
+ log.info(`Config reloaded: ${hotReloadable.length} settings updated`);
100
+ if (hotReloadable.length > 0) {
101
+ log.info(` Hot-reloaded: ${hotReloadable.join(', ')}`);
102
+ }
103
+ if (requiresRestart.length > 0) {
104
+ log.warn(` Requires restart: ${requiresRestart.join(', ')}`);
105
+ }
106
+ return {
107
+ success: true,
108
+ reloaded: hotReloadable,
109
+ requiresRestart
110
+ };
111
+ }
112
+ catch (error) {
113
+ const message = error instanceof Error ? error.message : String(error);
114
+ log.error(`Config reload failed: ${message}`);
115
+ return {
116
+ success: false,
117
+ reloaded: [],
118
+ requiresRestart: [],
119
+ error: message
120
+ };
121
+ }
122
+ }
123
+ }
124
+ // Singleton instance
125
+ let instance = null;
126
+ /**
127
+ * Initialize the config manager
128
+ */
129
+ export function initConfigManager(configPath) {
130
+ instance = new ConfigManager(configPath);
131
+ return instance;
132
+ }
133
+ /**
134
+ * Get the config manager instance
135
+ */
136
+ export function getConfigManager() {
137
+ if (!instance) {
138
+ throw new Error('ConfigManager not initialized. Call initConfigManager first.');
139
+ }
140
+ return instance;
141
+ }
142
+ /**
143
+ * Get the current config (convenience function)
144
+ */
145
+ export function getCurrentConfig() {
146
+ return getConfigManager().getConfig();
147
+ }
148
+ /**
149
+ * Reload the config (convenience function)
150
+ */
151
+ export function reloadConfig() {
152
+ return getConfigManager().reload();
153
+ }
154
+ //# sourceMappingURL=config-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/daemon/config-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import httpProxy from 'http-proxy';
3
+ /**
4
+ * Build clean headers object (filter out undefined values and encoding headers)
5
+ */
6
+ export declare function buildCleanHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string | string[]>;
7
+ /**
8
+ * Transform HTML response with toolbar injection and optional gzip compression
9
+ */
10
+ export declare function transformHtmlResponse(originalHtml: string, supportsGzip: boolean, basePath: string): {
11
+ body: Buffer;
12
+ headers: Record<string, string>;
13
+ };
14
+ /**
15
+ * Check if content type is HTML
16
+ */
17
+ export declare function isHtmlContentType(contentType: string): boolean;
18
+ /**
19
+ * Check if client supports gzip encoding
20
+ */
21
+ export declare function supportsGzipEncoding(acceptEncoding: string): boolean;
22
+ export declare const proxy: httpProxy<IncomingMessage, ServerResponse<IncomingMessage>>;
23
+ /**
24
+ * Proxy HTTP request to session backend
25
+ */
26
+ export declare function proxyToSession(req: IncomingMessage, res: ServerResponse, port: number, basePath: string): void;
27
+ //# sourceMappingURL=http-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-proxy.d.ts","sourceRoot":"","sources":["../../src/daemon/http-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,SAAS,MAAM,YAAY,CAAC;AAKnC;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAQnC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,OAAO,EACrB,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAYnD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAEpE;AAGD,eAAO,MAAM,KAAK,6DAGhB,CAAC;AAgEH;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,IAAI,CAkBN"}
@@ -0,0 +1,110 @@
1
+ import { gzipSync } from 'node:zlib';
2
+ import { createLogger } from '../utils/logger.js';
3
+ import httpProxy from 'http-proxy';
4
+ import { injectToolbar } from './toolbar/index.js';
5
+ const log = createLogger('proxy');
6
+ /**
7
+ * Build clean headers object (filter out undefined values and encoding headers)
8
+ */
9
+ export function buildCleanHeaders(headers) {
10
+ const cleanHeaders = {};
11
+ for (const [key, value] of Object.entries(headers)) {
12
+ if (value !== undefined && key !== 'content-encoding' && key !== 'transfer-encoding') {
13
+ cleanHeaders[key] = value;
14
+ }
15
+ }
16
+ return cleanHeaders;
17
+ }
18
+ /**
19
+ * Transform HTML response with toolbar injection and optional gzip compression
20
+ */
21
+ export function transformHtmlResponse(originalHtml, supportsGzip, basePath) {
22
+ const modifiedHtml = injectToolbar(originalHtml, basePath);
23
+ const headers = {};
24
+ if (supportsGzip) {
25
+ const compressed = gzipSync(modifiedHtml);
26
+ headers['content-encoding'] = 'gzip';
27
+ headers['content-length'] = String(compressed.length);
28
+ return { body: compressed, headers };
29
+ }
30
+ headers['content-length'] = String(Buffer.byteLength(modifiedHtml));
31
+ return { body: Buffer.from(modifiedHtml), headers };
32
+ }
33
+ /**
34
+ * Check if content type is HTML
35
+ */
36
+ export function isHtmlContentType(contentType) {
37
+ return contentType.includes('text/html');
38
+ }
39
+ /**
40
+ * Check if client supports gzip encoding
41
+ */
42
+ export function supportsGzipEncoding(acceptEncoding) {
43
+ return acceptEncoding.includes('gzip');
44
+ }
45
+ // Create proxy server for HTTP only
46
+ export const proxy = httpProxy.createProxyServer({
47
+ changeOrigin: true,
48
+ xfwd: true
49
+ });
50
+ // Handle proxy errors
51
+ proxy.on('error', (err, req, res) => {
52
+ const url = req.url ?? 'unknown';
53
+ log.error(`Proxy error for ${url}: ${err.message}`);
54
+ if (res && 'writeHead' in res && typeof res.writeHead === 'function') {
55
+ const httpRes = res;
56
+ if (!httpRes.headersSent) {
57
+ httpRes.writeHead(502, { 'Content-Type': 'text/html; charset=utf-8' });
58
+ httpRes.end(`<!DOCTYPE html>
59
+ <html><head><title>502 Bad Gateway</title></head>
60
+ <body style="font-family:sans-serif;padding:2em;">
61
+ <h1>502 Bad Gateway</h1>
62
+ <p>The ttyd session may have been stopped or is not responding.</p>
63
+ <p>Try <a href="javascript:location.reload()">reloading</a> or check session status with: <code>ttyd-mux status</code></p>
64
+ </body></html>`);
65
+ }
66
+ }
67
+ });
68
+ // Handle selfHandleResponse for HTML injection
69
+ proxy.on('proxyRes', (proxyRes, req, res) => {
70
+ const httpRes = res;
71
+ // Check if this is a self-handled HTML response
72
+ const contentType = proxyRes.headers['content-type'] ?? '';
73
+ if (!isHtmlContentType(contentType)) {
74
+ // Not HTML, just pipe through
75
+ httpRes.writeHead(proxyRes.statusCode ?? 200, proxyRes.headers);
76
+ proxyRes.pipe(httpRes);
77
+ return;
78
+ }
79
+ const extReq = req;
80
+ const acceptEncoding = extReq.originalAcceptEncoding ?? '';
81
+ const supportsGzip = supportsGzipEncoding(acceptEncoding);
82
+ const basePath = extReq.toolbarBasePath ?? '/ttyd-mux';
83
+ // Collect HTML body and inject toolbar
84
+ const chunks = [];
85
+ proxyRes.on('data', (chunk) => chunks.push(chunk));
86
+ proxyRes.on('end', () => {
87
+ const originalHtml = Buffer.concat(chunks).toString('utf-8');
88
+ const { body, headers: transformHeaders } = transformHtmlResponse(originalHtml, supportsGzip, basePath);
89
+ // Build clean headers object
90
+ const headers = buildCleanHeaders(proxyRes.headers);
91
+ Object.assign(headers, transformHeaders);
92
+ httpRes.writeHead(proxyRes.statusCode ?? 200, headers);
93
+ httpRes.end(body);
94
+ });
95
+ });
96
+ /**
97
+ * Proxy HTTP request to session backend
98
+ */
99
+ export function proxyToSession(req, res, port, basePath) {
100
+ const target = `http://localhost:${port}`;
101
+ log.debug(`Proxying ${req.url} to ${target}`);
102
+ const extReq = req;
103
+ extReq.originalAcceptEncoding = req.headers['accept-encoding'];
104
+ extReq.toolbarBasePath = basePath;
105
+ // Request uncompressed response for HTML injection (identity = no encoding)
106
+ req.headers['accept-encoding'] = 'identity';
107
+ // Always use selfHandleResponse to avoid conflicts with proxyRes handler
108
+ proxy.web(req, res, { target, selfHandleResponse: true });
109
+ }
110
+ //# sourceMappingURL=http-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-proxy.js","sourceRoot":"","sources":["../../src/daemon/http-proxy.ts"],"names":[],"mappings":"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"}