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
@@ -1,89 +1,194 @@
1
1
  import { existsSync, mkdirSync, unlinkSync } from 'node:fs';
2
2
  import { createServer as createUnixServer } from 'node:net';
3
- import { loadConfig } from '../config/config.js';
4
3
  import { clearDaemonState, getSocketPath, getStateDir, setDaemonState } from '../config/state.js';
5
- import { createDaemonServer } from './server.js';
6
- import { stopAllSessions } from './session-manager.js';
4
+ import { createLogger } from '../utils/logger.js';
5
+ import { getCurrentConfig, initConfigManager, reloadConfig } from './config-manager.js';
6
+ import { createDaemonServer, setConfigGetter } from './server.js';
7
+ import { sessionManager } from './session-manager.js';
8
+ const log = createLogger('daemon');
9
+ /**
10
+ * Clean up a socket file if it exists
11
+ */
12
+ function cleanupSocketFile(socketPath, label = 'socket') {
13
+ if (existsSync(socketPath)) {
14
+ try {
15
+ unlinkSync(socketPath);
16
+ log.info(`Removed old ${label}: ${socketPath}`);
17
+ }
18
+ catch (err) {
19
+ log.warn(`Failed to remove old ${label} ${socketPath}: ${err}`);
20
+ }
21
+ }
22
+ }
23
+ /**
24
+ * Clean up multiple socket files
25
+ */
26
+ function cleanupSocketFiles(socketPaths, label = 'socket') {
27
+ for (const socketPath of socketPaths) {
28
+ cleanupSocketFile(socketPath, label);
29
+ }
30
+ }
31
+ /**
32
+ * Revalidate existing sessions from previous daemon instance
33
+ */
34
+ function revalidateExistingSessions() {
35
+ const { valid, removed } = sessionManager.revalidateSessions();
36
+ if (valid.length === 0 && removed.length === 0) {
37
+ return;
38
+ }
39
+ log.info(`Session revalidation: ${valid.length} active, ${removed.length} removed`);
40
+ if (valid.length > 0) {
41
+ console.log(`Recovered ${valid.length} existing session(s)`);
42
+ }
43
+ if (removed.length > 0) {
44
+ console.log(`Cleaned up ${removed.length} dead session(s)`);
45
+ }
46
+ }
7
47
  export async function startDaemon(options = {}) {
8
- const config = loadConfig(options.configPath);
48
+ log.info('Starting daemon...');
49
+ revalidateExistingSessions();
50
+ // Set up global error handlers
51
+ process.on('uncaughtException', (error) => {
52
+ log.error(`Uncaught exception: ${error.message}`, error.stack);
53
+ process.exit(1);
54
+ });
55
+ process.on('unhandledRejection', (reason, promise) => {
56
+ log.error(`Unhandled rejection at: ${promise}, reason: ${reason}`);
57
+ });
58
+ const configManager = initConfigManager(options.configPath);
59
+ const config = configManager.getConfig();
60
+ log.info(`Config loaded: port=${config.daemon_port}, base_path=${config.base_path}`);
61
+ // Set up config getter for hot-reload support
62
+ setConfigGetter(getCurrentConfig);
9
63
  const socketPath = getSocketPath();
10
64
  // Ensure state directory exists
11
65
  const stateDir = getStateDir();
12
66
  if (!existsSync(stateDir)) {
13
67
  mkdirSync(stateDir, { recursive: true });
68
+ log.info(`Created state directory: ${stateDir}`);
14
69
  }
15
- // Clean up old socket if exists
16
- if (existsSync(socketPath)) {
17
- try {
18
- unlinkSync(socketPath);
19
- }
20
- catch {
21
- // Ignore
22
- }
23
- }
70
+ // Clean up old sockets
71
+ cleanupSocketFile(socketPath, 'CLI socket');
24
72
  // Create HTTP servers for each listen address
25
73
  const listenAddresses = config.listen_addresses ?? ['127.0.0.1', '::1'];
74
+ const listenSockets = config.listen_sockets ?? [];
26
75
  const httpServers = listenAddresses.map(() => createDaemonServer(config));
27
- // Start HTTP servers
76
+ // Create HTTP servers for Unix sockets
77
+ const socketServers = listenSockets.map(() => createDaemonServer(config));
78
+ // Clean up old HTTP socket files
79
+ cleanupSocketFiles(listenSockets, 'HTTP socket');
80
+ // Start HTTP servers on TCP
28
81
  let firstServer = true;
29
82
  for (let i = 0; i < listenAddresses.length; i++) {
30
83
  const address = listenAddresses[i];
31
84
  const server = httpServers[i];
32
- if (!address || !server)
85
+ if (!(address && server)) {
33
86
  continue;
87
+ }
88
+ server.on('error', (err) => {
89
+ log.error(`HTTP server error on ${address}: ${err.message}`, err.stack);
90
+ });
34
91
  server.listen(config.daemon_port, address, () => {
92
+ log.info(`HTTP server listening on ${address}:${config.daemon_port}`);
35
93
  if (firstServer) {
36
94
  firstServer = false;
37
95
  console.log(`ttyd-mux daemon started on http://localhost:${config.daemon_port}${config.base_path}/`);
38
96
  console.log(` Listening on: ${listenAddresses.join(', ')}`);
97
+ if (listenSockets.length > 0) {
98
+ console.log(` Unix sockets: ${listenSockets.join(', ')}`);
99
+ }
39
100
  // Save daemon state
40
101
  setDaemonState({
41
102
  pid: process.pid,
42
103
  port: config.daemon_port,
43
104
  started_at: new Date().toISOString()
44
105
  });
106
+ log.info(`Daemon state saved: pid=${process.pid}`);
45
107
  }
46
108
  });
47
109
  }
110
+ // Start HTTP servers on Unix sockets
111
+ for (let i = 0; i < listenSockets.length; i++) {
112
+ const sockPath = listenSockets[i];
113
+ const server = socketServers[i];
114
+ if (!(sockPath && server)) {
115
+ continue;
116
+ }
117
+ server.on('error', (err) => {
118
+ log.error(`HTTP socket server error on ${sockPath}: ${err.message}`, err.stack);
119
+ });
120
+ server.listen(sockPath, () => {
121
+ log.info(`HTTP server listening on unix:${sockPath}`);
122
+ });
123
+ }
48
124
  // Create Unix socket for CLI communication
49
125
  const unixServer = createUnixServer((socket) => {
50
126
  socket.on('data', (data) => {
51
127
  const command = data.toString().trim();
128
+ log.debug(`Unix socket received command: ${command}`);
52
129
  if (command === 'ping') {
53
130
  socket.write('pong');
54
131
  }
55
132
  else if (command === 'shutdown') {
56
133
  socket.write('ok');
57
- shutdown();
134
+ shutdown(false);
135
+ }
136
+ else if (command === 'shutdown-with-sessions') {
137
+ socket.write('ok');
138
+ shutdown(true);
139
+ }
140
+ else if (command === 'reload') {
141
+ const result = reloadConfig();
142
+ socket.write(JSON.stringify(result));
58
143
  }
59
144
  socket.end();
60
145
  });
146
+ socket.on('error', (err) => {
147
+ log.error(`Unix socket connection error: ${err.message}`);
148
+ });
149
+ });
150
+ unixServer.on('error', (err) => {
151
+ log.error(`Unix server error: ${err.message}`, err.stack);
61
152
  });
62
153
  unixServer.listen(socketPath, () => {
154
+ log.info(`Unix socket listening: ${socketPath}`);
63
155
  console.log(`Unix socket: ${socketPath}`);
64
156
  });
65
157
  // Handle shutdown signals
66
- const shutdown = () => {
158
+ const shutdown = (stopSessions = false) => {
159
+ log.info(`Shutdown requested (stopSessions=${stopSessions})`);
67
160
  console.log('\nShutting down...');
68
- stopAllSessions();
161
+ if (stopSessions) {
162
+ sessionManager.stopAllSessions();
163
+ log.info('All sessions stopped');
164
+ }
165
+ else {
166
+ log.info('Sessions preserved (daemon-only shutdown)');
167
+ }
69
168
  clearDaemonState();
169
+ log.info('Daemon state cleared');
70
170
  for (const server of httpServers) {
71
171
  server.close();
72
172
  }
73
- unixServer.close();
74
- // Clean up socket file
75
- if (existsSync(socketPath)) {
76
- try {
77
- unlinkSync(socketPath);
78
- }
79
- catch {
80
- // Ignore
81
- }
173
+ for (const server of socketServers) {
174
+ server.close();
82
175
  }
176
+ unixServer.close();
177
+ log.info('Servers closed');
178
+ // Clean up socket files
179
+ cleanupSocketFile(socketPath, 'CLI socket');
180
+ cleanupSocketFiles(listenSockets, 'HTTP socket');
181
+ log.info('Daemon shutdown complete');
83
182
  process.exit(0);
84
183
  };
85
- process.on('SIGINT', shutdown);
86
- process.on('SIGTERM', shutdown);
184
+ process.on('SIGINT', () => {
185
+ log.info('Received SIGINT');
186
+ shutdown();
187
+ });
188
+ process.on('SIGTERM', () => {
189
+ log.info('Received SIGTERM');
190
+ shutdown();
191
+ });
87
192
  // Keep process running
88
193
  if (!options.foreground) {
89
194
  // When not in foreground, detach stdio
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared utilities for portal HTML generation
3
+ */
4
+ /**
5
+ * Escape HTML special characters to prevent XSS
6
+ */
7
+ export declare function escapeHtml(str: string): string;
8
+ /**
9
+ * Generate PWA meta tags and links
10
+ */
11
+ export declare function generatePwaHead(basePath: string): string;
12
+ /**
13
+ * Generate Service Worker registration script
14
+ */
15
+ export declare function generateSwRegistration(basePath: string): string;
16
+ /**
17
+ * Common CSS styles for portal pages
18
+ */
19
+ export declare const portalStyles = "\n * {\n box-sizing: border-box;\n }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n max-width: 800px;\n margin: 0 auto;\n padding: 2rem;\n background: #1a1a2e;\n color: #eee;\n min-height: 100vh;\n }\n h1 {\n color: #00d9ff;\n margin-bottom: 0.5rem;\n }\n .subtitle {\n color: #888;\n margin-bottom: 2rem;\n }\n ul {\n list-style: none;\n padding: 0;\n margin: 0;\n }\n .session {\n margin: 0.5rem 0;\n }\n .session a {\n display: block;\n padding: 1rem;\n background: #16213e;\n border-radius: 8px;\n text-decoration: none;\n color: #eee;\n transition: background 0.2s, transform 0.1s;\n }\n .session a:hover {\n background: #1f3460;\n transform: translateX(4px);\n }\n .name {\n font-weight: 600;\n font-size: 1.1rem;\n color: #00d9ff;\n }\n .info {\n display: block;\n font-size: 0.85rem;\n color: #888;\n margin-top: 0.25rem;\n }\n .no-sessions {\n color: #888;\n padding: 2rem;\n text-align: center;\n background: #16213e;\n border-radius: 8px;\n }\n code {\n background: #0f0f23;\n padding: 0.2rem 0.4rem;\n border-radius: 4px;\n font-family: \"SF Mono\", Monaco, \"Cascadia Code\", monospace;\n }";
20
+ //# sourceMappingURL=portal-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-utils.d.ts","sourceRoot":"","sources":["../../src/daemon/portal-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASxD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,o7CAiEnB,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Shared utilities for portal HTML generation
3
+ */
4
+ /**
5
+ * Escape HTML special characters to prevent XSS
6
+ */
7
+ export function escapeHtml(str) {
8
+ return str
9
+ .replace(/&/g, '&amp;')
10
+ .replace(/</g, '&lt;')
11
+ .replace(/>/g, '&gt;')
12
+ .replace(/"/g, '&quot;')
13
+ .replace(/'/g, '&#039;');
14
+ }
15
+ /**
16
+ * Generate PWA meta tags and links
17
+ */
18
+ export function generatePwaHead(basePath) {
19
+ return `
20
+ <meta name="theme-color" content="#00d9ff">
21
+ <meta name="apple-mobile-web-app-capable" content="yes">
22
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
23
+ <meta name="apple-mobile-web-app-title" content="ttyd-mux">
24
+ <link rel="manifest" href="${basePath}/manifest.json">
25
+ <link rel="apple-touch-icon" href="${basePath}/icon-192.png">
26
+ <link rel="icon" type="image/svg+xml" href="${basePath}/icon.svg">`;
27
+ }
28
+ /**
29
+ * Generate Service Worker registration script
30
+ */
31
+ export function generateSwRegistration(basePath) {
32
+ return `
33
+ <script>
34
+ if ('serviceWorker' in navigator) {
35
+ navigator.serviceWorker.register('${basePath}/sw.js')
36
+ .catch(err => console.warn('SW registration failed:', err));
37
+ }
38
+ </script>`;
39
+ }
40
+ /**
41
+ * Common CSS styles for portal pages
42
+ */
43
+ export const portalStyles = `
44
+ * {
45
+ box-sizing: border-box;
46
+ }
47
+ body {
48
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
49
+ max-width: 800px;
50
+ margin: 0 auto;
51
+ padding: 2rem;
52
+ background: #1a1a2e;
53
+ color: #eee;
54
+ min-height: 100vh;
55
+ }
56
+ h1 {
57
+ color: #00d9ff;
58
+ margin-bottom: 0.5rem;
59
+ }
60
+ .subtitle {
61
+ color: #888;
62
+ margin-bottom: 2rem;
63
+ }
64
+ ul {
65
+ list-style: none;
66
+ padding: 0;
67
+ margin: 0;
68
+ }
69
+ .session {
70
+ margin: 0.5rem 0;
71
+ }
72
+ .session a {
73
+ display: block;
74
+ padding: 1rem;
75
+ background: #16213e;
76
+ border-radius: 8px;
77
+ text-decoration: none;
78
+ color: #eee;
79
+ transition: background 0.2s, transform 0.1s;
80
+ }
81
+ .session a:hover {
82
+ background: #1f3460;
83
+ transform: translateX(4px);
84
+ }
85
+ .name {
86
+ font-weight: 600;
87
+ font-size: 1.1rem;
88
+ color: #00d9ff;
89
+ }
90
+ .info {
91
+ display: block;
92
+ font-size: 0.85rem;
93
+ color: #888;
94
+ margin-top: 0.25rem;
95
+ }
96
+ .no-sessions {
97
+ color: #888;
98
+ padding: 2rem;
99
+ text-align: center;
100
+ background: #16213e;
101
+ border-radius: 8px;
102
+ }
103
+ code {
104
+ background: #0f0f23;
105
+ padding: 0.2rem 0.4rem;
106
+ border-radius: 4px;
107
+ font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
108
+ }`;
109
+ //# sourceMappingURL=portal-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-utils.js","sourceRoot":"","sources":["../../src/daemon/portal-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO;;;;;+BAKsB,QAAQ;uCACA,QAAQ;gDACC,QAAQ,aAAa,CAAC;AACtE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,OAAO;;;0CAGiC,QAAQ;;;YAGtC,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAiEtB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/daemon/portal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE/D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAoHnF;AAWD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE1D"}
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/daemon/portal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAuB9D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAoDnF;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE1D"}
@@ -1,5 +1,21 @@
1
- import { getFullPath } from '../config/config.js';
1
+ import { getFullPath, normalizeBasePath } from '../config/config.js';
2
+ import { escapeHtml, generatePwaHead, generateSwRegistration, portalStyles } from './portal-utils.js';
3
+ /**
4
+ * Generate auto-reload script for portal page
5
+ * Reloads the page when the tab becomes visible to refresh session list
6
+ */
7
+ function generateAutoReloadScript() {
8
+ return `
9
+ <script>
10
+ document.addEventListener('visibilitychange', function() {
11
+ if (!document.hidden) {
12
+ location.reload();
13
+ }
14
+ });
15
+ </script>`;
16
+ }
2
17
  export function generatePortalHtml(config, sessions) {
18
+ const basePath = normalizeBasePath(config.base_path);
3
19
  const sessionItems = sessions
4
20
  .map((session) => {
5
21
  const fullPath = getFullPath(config, session.path);
@@ -19,73 +35,8 @@ export function generatePortalHtml(config, sessions) {
19
35
  <head>
20
36
  <meta charset="UTF-8">
21
37
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
- <title>ttyd-mux</title>
23
- <style>
24
- * {
25
- box-sizing: border-box;
26
- }
27
- body {
28
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
29
- max-width: 800px;
30
- margin: 0 auto;
31
- padding: 2rem;
32
- background: #1a1a2e;
33
- color: #eee;
34
- min-height: 100vh;
35
- }
36
- h1 {
37
- color: #00d9ff;
38
- margin-bottom: 0.5rem;
39
- }
40
- .subtitle {
41
- color: #888;
42
- margin-bottom: 2rem;
43
- }
44
- ul {
45
- list-style: none;
46
- padding: 0;
47
- margin: 0;
48
- }
49
- .session {
50
- margin: 0.5rem 0;
51
- }
52
- .session a {
53
- display: block;
54
- padding: 1rem;
55
- background: #16213e;
56
- border-radius: 8px;
57
- text-decoration: none;
58
- color: #eee;
59
- transition: background 0.2s, transform 0.1s;
60
- }
61
- .session a:hover {
62
- background: #1f3460;
63
- transform: translateX(4px);
64
- }
65
- .name {
66
- font-weight: 600;
67
- font-size: 1.1rem;
68
- color: #00d9ff;
69
- }
70
- .info {
71
- display: block;
72
- font-size: 0.85rem;
73
- color: #888;
74
- margin-top: 0.25rem;
75
- }
76
- .no-sessions {
77
- color: #888;
78
- padding: 2rem;
79
- text-align: center;
80
- background: #16213e;
81
- border-radius: 8px;
82
- }
83
- code {
84
- background: #0f0f23;
85
- padding: 0.2rem 0.4rem;
86
- border-radius: 4px;
87
- font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
88
- }
38
+ <title>ttyd-mux</title>${generatePwaHead(basePath)}
39
+ <style>${portalStyles}
89
40
  .refresh {
90
41
  margin-top: 2rem;
91
42
  text-align: center;
@@ -108,19 +59,11 @@ ${sessionItems}
108
59
  ${noSessions}
109
60
  <div class="refresh">
110
61
  <a href="javascript:location.reload()">Refresh</a>
111
- </div>
62
+ </div>${generateSwRegistration(basePath)}${generateAutoReloadScript()}
112
63
  </body>
113
64
  </html>
114
65
  `;
115
66
  }
116
- function escapeHtml(str) {
117
- return str
118
- .replace(/&/g, '&amp;')
119
- .replace(/</g, '&lt;')
120
- .replace(/>/g, '&gt;')
121
- .replace(/"/g, '&quot;')
122
- .replace(/'/g, '&#039;');
123
- }
124
67
  export function generateJsonResponse(data) {
125
68
  return JSON.stringify(data, null, 2);
126
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"portal.js","sourceRoot":"","sources":["../../src/daemon/portal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,QAAwB;IACzE,MAAM,YAAY,GAAG,QAAQ;SAC1B,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACf,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO;mBACM,QAAQ;+BACI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;gCACvB,OAAO,CAAC,IAAI,MAAM,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;;YAE7D,CAAC;IACT,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,UAAU,GACd,QAAQ,CAAC,MAAM,KAAK,CAAC;QACnB,CAAC,CAAC,2FAA2F;QAC7F,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyFP,YAAY;;IAEV,UAAU;;;;;;CAMb,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"portal.js","sourceRoot":"","sources":["../../src/daemon/portal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEpE,OAAO,EACL,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,YAAY,EACb,MAAM,mBAAmB,CAAC;AAE3B;;;GAGG;AACH,SAAS,wBAAwB;IAC/B,OAAO;;;;;;;YAOG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,QAAwB;IACzE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,QAAQ;SAC1B,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACf,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO;mBACM,QAAQ;+BACI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;gCACvB,OAAO,CAAC,IAAI,MAAM,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;;YAE7D,CAAC;IACT,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,UAAU,GACd,QAAQ,CAAC,MAAM,KAAK,CAAC;QACnB,CAAC,CAAC,2FAA2F;QAC7F,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;;;;;2BAKkB,eAAe,CAAC,QAAQ,CAAC;WACzC,YAAY;;;;;;;;;;;;;;;;;;EAkBrB,YAAY;;IAEV,UAAU;;;UAGJ,sBAAsB,CAAC,QAAQ,CAAC,GAAG,wBAAwB,EAAE;;;CAGtE,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * PWA (Progressive Web App) support for ttyd-mux
3
+ *
4
+ * Provides:
5
+ * - Web App Manifest for "Add to Home Screen" functionality
6
+ * - Service Worker for installability
7
+ * - App icons (SVG with PNG fallbacks)
8
+ */
9
+ /**
10
+ * Generate Web App Manifest JSON
11
+ */
12
+ export declare function generateManifest(basePath: string): object;
13
+ /**
14
+ * Service Worker script
15
+ *
16
+ * Minimal implementation for PWA installability.
17
+ * Uses network-first strategy since terminal requires online connectivity.
18
+ */
19
+ export declare const serviceWorkerScript = "// ttyd-mux Service Worker\nself.addEventListener('install', (event) => {\n // Skip waiting to activate immediately\n self.skipWaiting();\n});\n\nself.addEventListener('activate', (event) => {\n // Claim all clients immediately\n event.waitUntil(clients.claim());\n});\n\nself.addEventListener('fetch', (event) => {\n // Network-first strategy - terminal requires online connectivity\n event.respondWith(fetch(event.request));\n});\n";
20
+ /**
21
+ * SVG icon for ttyd-mux
22
+ *
23
+ * Simple terminal prompt icon (>_) with the app's color scheme.
24
+ */
25
+ export declare const iconSvg = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\">\n <rect width=\"512\" height=\"512\" rx=\"64\" fill=\"#1a1a2e\"/>\n <text x=\"256\" y=\"320\" font-family=\"monospace\" font-size=\"200\" font-weight=\"bold\" fill=\"#00d9ff\" text-anchor=\"middle\">&gt;_</text>\n</svg>";
26
+ /**
27
+ * Generate PNG icon from SVG using canvas
28
+ *
29
+ * Note: This is a simplified approach. For production, consider pre-generating
30
+ * PNG files or using a proper SVG-to-PNG library.
31
+ *
32
+ * The PNG data below is a pre-rendered version of the SVG icon.
33
+ */
34
+ export declare const icon192Base64: string;
35
+ export declare const icon512Base64: string;
36
+ /**
37
+ * Get PNG icon as Buffer
38
+ */
39
+ export declare function getIconPng(size: 192 | 512): Buffer;
40
+ /**
41
+ * Get SVG icon as string
42
+ */
43
+ export declare function getIconSvg(): string;
44
+ /**
45
+ * Get Service Worker script
46
+ */
47
+ export declare function getServiceWorker(): string;
48
+ /**
49
+ * Get manifest JSON string
50
+ */
51
+ export declare function getManifestJson(basePath: string): string;
52
+ //# sourceMappingURL=pwa.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../../src/daemon/pwa.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6BzD;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,4bAe/B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,qSAGb,CAAC;AAER;;;;;;;GAOG;AAIH,eAAO,MAAM,aAAa,QAA6B,CAAC;AAGxD,eAAO,MAAM,aAAa,QAA6B,CAAC;AAsJxD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAGlD;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD"}