nterminal 1.2.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 (197) hide show
  1. package/.env.example +12 -0
  2. package/LICENSE +674 -0
  3. package/README.md +181 -0
  4. package/assets/brand/app-icon-1024.png +0 -0
  5. package/assets/brand/app-icon-384.png +0 -0
  6. package/assets/brand/apple-touch-icon-360.png +0 -0
  7. package/assets/brand/favicon-32.png +0 -0
  8. package/assets/brand/favicon-64.png +0 -0
  9. package/assets/brand/favicon-96.png +0 -0
  10. package/assets/brand/favicon.svg +4 -0
  11. package/assets/brand/nterminal-mark-64.png +0 -0
  12. package/assets/brand/nterminal-mark.svg +4 -0
  13. package/assets/brand/nterminal-wordmark-486x68.png +0 -0
  14. package/assets/brand/nterminal-wordmark.svg +3 -0
  15. package/assets/screenshot/scr.png +0 -0
  16. package/bin/nterminal.js +114 -0
  17. package/dist/client/apple-touch-icon.png +0 -0
  18. package/dist/client/assets/MarkdownPreview-BeDi-V7k.js +29 -0
  19. package/dist/client/assets/MesloLGS-NF-Bold-Italic-DwFsXcwX.ttf +0 -0
  20. package/dist/client/assets/MesloLGS-NF-Bold-kN-HYz-g.ttf +0 -0
  21. package/dist/client/assets/MesloLGS-NF-Italic-CMg1T6-G.ttf +0 -0
  22. package/dist/client/assets/MesloLGS-NF-Regular-Cxr8pvCI.ttf +0 -0
  23. package/dist/client/assets/index-BQkKYjXb.js +33 -0
  24. package/dist/client/assets/index-WqeS39wU.css +1 -0
  25. package/dist/client/assets/notifications/character-2258.mp4 +0 -0
  26. package/dist/client/assets/notifications/character-2260.mp4 +0 -0
  27. package/dist/client/assets/notifications/character-2272.mp4 +0 -0
  28. package/dist/client/brand/nterminal-mark-64.png +0 -0
  29. package/dist/client/brand/nterminal-mark.svg +4 -0
  30. package/dist/client/brand/nterminal-wordmark-486x68.png +0 -0
  31. package/dist/client/brand/nterminal-wordmark.svg +3 -0
  32. package/dist/client/icons/app-icon-1024.png +0 -0
  33. package/dist/client/icons/app-icon-384.png +0 -0
  34. package/dist/client/icons/favicon-32.png +0 -0
  35. package/dist/client/icons/favicon-64.png +0 -0
  36. package/dist/client/icons/favicon-96.png +0 -0
  37. package/dist/client/icons/favicon.svg +4 -0
  38. package/dist/client/index.html +21 -0
  39. package/dist/client/manifest.webmanifest +24 -0
  40. package/dist/scripts/generate-secrets.js +3 -0
  41. package/dist/scripts/generate-secrets.js.map +1 -0
  42. package/dist/scripts/onboarding.js +814 -0
  43. package/dist/scripts/onboarding.js.map +1 -0
  44. package/dist/scripts/proxySetup.js +1007 -0
  45. package/dist/scripts/proxySetup.js.map +1 -0
  46. package/dist/server/agent/agentAuth.d.ts +6 -0
  47. package/dist/server/agent/agentAuth.js +35 -0
  48. package/dist/server/agent/agentAuth.js.map +1 -0
  49. package/dist/server/agent/agentProxy.d.ts +5 -0
  50. package/dist/server/agent/agentProxy.js +63 -0
  51. package/dist/server/agent/agentProxy.js.map +1 -0
  52. package/dist/server/agent/agentRoutes.d.ts +9 -0
  53. package/dist/server/agent/agentRoutes.js +327 -0
  54. package/dist/server/agent/agentRoutes.js.map +1 -0
  55. package/dist/server/agent/agentWebSocketProxy.d.ts +3 -0
  56. package/dist/server/agent/agentWebSocketProxy.js +65 -0
  57. package/dist/server/agent/agentWebSocketProxy.js.map +1 -0
  58. package/dist/server/auth/authService.d.ts +100 -0
  59. package/dist/server/auth/authService.js +415 -0
  60. package/dist/server/auth/authService.js.map +1 -0
  61. package/dist/server/auth/cookies.d.ts +11 -0
  62. package/dist/server/auth/cookies.js +39 -0
  63. package/dist/server/auth/cookies.js.map +1 -0
  64. package/dist/server/auth/ipMatch.d.ts +14 -0
  65. package/dist/server/auth/ipMatch.js +103 -0
  66. package/dist/server/auth/ipMatch.js.map +1 -0
  67. package/dist/server/auth/rateLimit.d.ts +17 -0
  68. package/dist/server/auth/rateLimit.js +25 -0
  69. package/dist/server/auth/rateLimit.js.map +1 -0
  70. package/dist/server/auth/totpService.d.ts +10 -0
  71. package/dist/server/auth/totpService.js +37 -0
  72. package/dist/server/auth/totpService.js.map +1 -0
  73. package/dist/server/config.d.ts +27 -0
  74. package/dist/server/config.js +138 -0
  75. package/dist/server/config.js.map +1 -0
  76. package/dist/server/files/fileExplorerService.d.ts +38 -0
  77. package/dist/server/files/fileExplorerService.js +551 -0
  78. package/dist/server/files/fileExplorerService.js.map +1 -0
  79. package/dist/server/files/rootToken.d.ts +51 -0
  80. package/dist/server/files/rootToken.js +139 -0
  81. package/dist/server/files/rootToken.js.map +1 -0
  82. package/dist/server/http.d.ts +13 -0
  83. package/dist/server/http.js +69 -0
  84. package/dist/server/http.js.map +1 -0
  85. package/dist/server/index.d.ts +1 -0
  86. package/dist/server/index.js +45 -0
  87. package/dist/server/index.js.map +1 -0
  88. package/dist/server/routes/agentManagementRoutes.d.ts +9 -0
  89. package/dist/server/routes/agentManagementRoutes.js +304 -0
  90. package/dist/server/routes/agentManagementRoutes.js.map +1 -0
  91. package/dist/server/routes/authRoutes.d.ts +10 -0
  92. package/dist/server/routes/authRoutes.js +95 -0
  93. package/dist/server/routes/authRoutes.js.map +1 -0
  94. package/dist/server/routes/fileRoutes.d.ts +11 -0
  95. package/dist/server/routes/fileRoutes.js +185 -0
  96. package/dist/server/routes/fileRoutes.js.map +1 -0
  97. package/dist/server/routes/notificationAssetRoutes.d.ts +9 -0
  98. package/dist/server/routes/notificationAssetRoutes.js +280 -0
  99. package/dist/server/routes/notificationAssetRoutes.js.map +1 -0
  100. package/dist/server/routes/securityRoutes.d.ts +7 -0
  101. package/dist/server/routes/securityRoutes.js +53 -0
  102. package/dist/server/routes/securityRoutes.js.map +1 -0
  103. package/dist/server/routes/socketBackpressure.d.ts +26 -0
  104. package/dist/server/routes/socketBackpressure.js +63 -0
  105. package/dist/server/routes/socketBackpressure.js.map +1 -0
  106. package/dist/server/routes/terminalLayoutRoutes.d.ts +9 -0
  107. package/dist/server/routes/terminalLayoutRoutes.js +108 -0
  108. package/dist/server/routes/terminalLayoutRoutes.js.map +1 -0
  109. package/dist/server/routes/terminalRoutes.d.ts +14 -0
  110. package/dist/server/routes/terminalRoutes.js +177 -0
  111. package/dist/server/routes/terminalRoutes.js.map +1 -0
  112. package/dist/server/routes/terminalWebSocket.d.ts +9 -0
  113. package/dist/server/routes/terminalWebSocket.js +129 -0
  114. package/dist/server/routes/terminalWebSocket.js.map +1 -0
  115. package/dist/server/routes/totpRoutes.d.ts +7 -0
  116. package/dist/server/routes/totpRoutes.js +46 -0
  117. package/dist/server/routes/totpRoutes.js.map +1 -0
  118. package/dist/server/routes/updateRoutes.d.ts +7 -0
  119. package/dist/server/routes/updateRoutes.js +24 -0
  120. package/dist/server/routes/updateRoutes.js.map +1 -0
  121. package/dist/server/routes/uploadRoutes.d.ts +9 -0
  122. package/dist/server/routes/uploadRoutes.js +95 -0
  123. package/dist/server/routes/uploadRoutes.js.map +1 -0
  124. package/dist/server/storage/fileStore.d.ts +90 -0
  125. package/dist/server/storage/fileStore.js +275 -0
  126. package/dist/server/storage/fileStore.js.map +1 -0
  127. package/dist/server/system/stats.d.ts +2 -0
  128. package/dist/server/system/stats.js +37 -0
  129. package/dist/server/system/stats.js.map +1 -0
  130. package/dist/server/terminal/NodePtyAdapter.d.ts +4 -0
  131. package/dist/server/terminal/NodePtyAdapter.js +14 -0
  132. package/dist/server/terminal/NodePtyAdapter.js.map +1 -0
  133. package/dist/server/terminal/PtyAdapter.d.ts +57 -0
  134. package/dist/server/terminal/PtyAdapter.js +2 -0
  135. package/dist/server/terminal/PtyAdapter.js.map +1 -0
  136. package/dist/server/terminal/TerminalManager.d.ts +74 -0
  137. package/dist/server/terminal/TerminalManager.js +561 -0
  138. package/dist/server/terminal/TerminalManager.js.map +1 -0
  139. package/dist/server/terminal/TmuxPtyAdapter.d.ts +25 -0
  140. package/dist/server/terminal/TmuxPtyAdapter.js +543 -0
  141. package/dist/server/terminal/TmuxPtyAdapter.js.map +1 -0
  142. package/dist/server/terminal/codexTranscriptSource.d.ts +9 -0
  143. package/dist/server/terminal/codexTranscriptSource.js +144 -0
  144. package/dist/server/terminal/codexTranscriptSource.js.map +1 -0
  145. package/dist/server/terminal/cwdResolver.d.ts +8 -0
  146. package/dist/server/terminal/cwdResolver.js +37 -0
  147. package/dist/server/terminal/cwdResolver.js.map +1 -0
  148. package/dist/server/terminal/outputBuffer.d.ts +7 -0
  149. package/dist/server/terminal/outputBuffer.js +17 -0
  150. package/dist/server/terminal/outputBuffer.js.map +1 -0
  151. package/dist/server/terminal/transcriptHistory.d.ts +7 -0
  152. package/dist/server/terminal/transcriptHistory.js +315 -0
  153. package/dist/server/terminal/transcriptHistory.js.map +1 -0
  154. package/dist/server/update/gitUpdate.d.ts +27 -0
  155. package/dist/server/update/gitUpdate.js +241 -0
  156. package/dist/server/update/gitUpdate.js.map +1 -0
  157. package/dist/server/uploads/uploadPaths.d.ts +18 -0
  158. package/dist/server/uploads/uploadPaths.js +116 -0
  159. package/dist/server/uploads/uploadPaths.js.map +1 -0
  160. package/dist/server/uploads/uploadService.d.ts +21 -0
  161. package/dist/server/uploads/uploadService.js +230 -0
  162. package/dist/server/uploads/uploadService.js.map +1 -0
  163. package/dist/shared/layoutState.d.ts +6 -0
  164. package/dist/shared/layoutState.js +115 -0
  165. package/dist/shared/layoutState.js.map +1 -0
  166. package/dist/shared/notificationAssets.d.ts +9 -0
  167. package/dist/shared/notificationAssets.js +27 -0
  168. package/dist/shared/notificationAssets.js.map +1 -0
  169. package/dist/shared/protocol.d.ts +308 -0
  170. package/dist/shared/protocol.js +29 -0
  171. package/dist/shared/protocol.js.map +1 -0
  172. package/dist/shared/types.d.ts +56 -0
  173. package/dist/shared/types.js +2 -0
  174. package/dist/shared/types.js.map +1 -0
  175. package/docs/assets/nterminal-workspace.png +0 -0
  176. package/docs/configuration.md +97 -0
  177. package/docs/features.md +126 -0
  178. package/docs/onboarding.md +122 -0
  179. package/docs/operations.md +112 -0
  180. package/docs/terminal-history.md +54 -0
  181. package/package.json +85 -0
  182. package/public/apple-touch-icon.png +0 -0
  183. package/public/assets/notifications/character-2258.mp4 +0 -0
  184. package/public/assets/notifications/character-2260.mp4 +0 -0
  185. package/public/assets/notifications/character-2272.mp4 +0 -0
  186. package/public/brand/nterminal-mark-64.png +0 -0
  187. package/public/brand/nterminal-mark.svg +4 -0
  188. package/public/brand/nterminal-wordmark-486x68.png +0 -0
  189. package/public/brand/nterminal-wordmark.svg +3 -0
  190. package/public/icons/app-icon-1024.png +0 -0
  191. package/public/icons/app-icon-384.png +0 -0
  192. package/public/icons/favicon-32.png +0 -0
  193. package/public/icons/favicon-64.png +0 -0
  194. package/public/icons/favicon-96.png +0 -0
  195. package/public/icons/favicon.svg +4 -0
  196. package/public/manifest.webmanifest +24 -0
  197. package/scripts/nterminalctl +588 -0
package/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # NTerminal
2
+
3
+ <img src="public/icons/app-icon-1024.png" alt="NTerminal app icon" width="96" height="96">
4
+
5
+ NTerminal turns your own machines into one secure browser workspace for real shells, Codex, Claude, files, and long-running terminal sessions.
6
+
7
+ <img src="assets/screenshot/scr.png" alt="NTerminal workspace with split terminal panes" width="100%">
8
+
9
+ NTerminal is a single-owner web terminal for machines you control. It is not a hosted shell product, not a SaaS app, and not a multi-user IDE.
10
+
11
+ ## How It Works
12
+
13
+ ```text
14
+ Browser
15
+ -> Main Server
16
+ -> Secondary Servers
17
+ ```
18
+
19
+ - **Main Server** serves the browser UI, owns login/session security, stores the server list, and proxies terminal/file traffic to secondary servers.
20
+ - **Secondary Servers** run on additional machines and expose their local shells/files to the main server after guided onboarding.
21
+ - Each server is a normal Node.js process connected to real host PTYs through `node-pty`.
22
+
23
+ ## Quick Start
24
+
25
+ Clone the repo on the machine that should become the **main server**, then run:
26
+
27
+ ```bash
28
+ npm run onboarding
29
+ ```
30
+
31
+ Choose `main`. Onboarding installs dependencies, writes `.env`, creates secrets, sets your password and TOTP, and can build/start the service.
32
+
33
+ To add another machine, clone the repo there and run the same command:
34
+
35
+ ```bash
36
+ npm run onboarding
37
+ ```
38
+
39
+ Choose `secondary`, enter the main server URL, log in once from the CLI, and the server registers itself in the main UI.
40
+
41
+ After onboarding finishes, open the URL printed in the final summary and log in with your password plus authenticator code.
42
+
43
+ See [docs/onboarding.md](docs/onboarding.md) for the full main/secondary setup flow, nginx/TLS automation, and local-only setup.
44
+
45
+ After NTerminal is published to npm, package installs can use:
46
+
47
+ ```bash
48
+ npm install -g nterminal
49
+ nterminal onboarding
50
+ ```
51
+
52
+ Package installs keep runtime config and state under `~/.nterminal` so package updates do not overwrite local sessions or secrets.
53
+ Use a user-writable npm global prefix if you want in-app updates to run without sudo.
54
+
55
+ ## Key Features
56
+
57
+ ### One Workspace For Multiple Servers
58
+
59
+ - Manage the main server and all secondary servers from one terminal UI.
60
+ - Switch servers from the sidebar without losing terminal layout or pane state.
61
+ - Keep separate tabs, split panes, detached terminals, and file context per server.
62
+ - Run fleet updates from the UI; git checkouts and npm package installs use their own safe update paths.
63
+
64
+ ### Secure Personal Access
65
+
66
+ - Password plus TOTP two-factor authentication.
67
+ - Single active session: a new login revokes the previous browser session.
68
+ - Session IP binding with subnet tolerance for common residential/CGNAT rotation.
69
+ - Trusted devices and trusted networks for repeat access from known places.
70
+ - Same-origin cookie and WebSocket checks for terminal/file mutation routes.
71
+
72
+ ### Persistent Terminal Workspace
73
+
74
+ - Real host shell PTYs with tabs and split panes.
75
+ - Refresh and temporary WebSocket reconnects reattach to active PTYs.
76
+ - Closing a tab or pane soft-detaches it into a restorable pill instead of killing the shell.
77
+ - Optional tmux-backed PTY persistence keeps shells alive across NTerminal Node restarts.
78
+ - Tab titles sync with tmux window names so labels survive detach/reattach cycles.
79
+
80
+ ### Codex / Claude Friendly History
81
+
82
+ - Mouse wheel scrolling moves terminal output history, not Codex/Claude prompt history.
83
+ - Older tmux scrollback loads incrementally when you reach the top.
84
+ - Supported Codex/Claude panes can continue into JSONL transcript history from `~/.codex/sessions` or `~/.claude/projects`.
85
+ - Scroll progress, a jump-to-bottom button, mobile drag handles, and follow-tail behavior keep large outputs usable.
86
+
87
+ ### Files, Uploads, And Clipboard
88
+
89
+ - Sidebar file explorer follows the selected pane's working directory.
90
+ - View/edit text and Markdown files, preview browser-supported files, and reject stale overwrites.
91
+ - Drag files or folders into the workspace to upload them into the active pane's cwd.
92
+ - Terminal Ctrl+C copies selected text and falls through to SIGINT when nothing is selected; Ctrl+V pastes clipboard text.
93
+
94
+ ### Completion Notifications
95
+
96
+ - In-app completion toasts appear when background commands finish.
97
+ - Toasts can auto-dismiss, stay until dismissed, or use custom MP4/WebM videos.
98
+ - Settings can preview built-in/uploaded videos, enable/disable them, and randomly choose from the enabled set.
99
+ - Clicking a completion toast jumps back to the server and pane that finished.
100
+ - Optional OS notifications are available when the page is hidden.
101
+
102
+ ### Mobile And PWA
103
+
104
+ - iPad home-screen metadata and icons are included.
105
+ - Mobile terminal key bar exposes Ctrl, Esc, Tab, and arrows when needed.
106
+ - Touch scrolling controls are tuned for long terminal output.
107
+
108
+ ### Simple npm Toolchain
109
+
110
+ - The project uses npm so source checkouts and package installs use the same package manager.
111
+ - `npm run onboarding` runs install automatically on fresh checkouts.
112
+ - The `nterminal` CLI entrypoint is prepared for global npm installs.
113
+
114
+ ## Requirements
115
+
116
+ - Node.js 22 or newer.
117
+ - Linux or macOS.
118
+ - Native build tools required by `node-pty`.
119
+ - Optional `tmux` for PTY persistence across Node restarts.
120
+
121
+ If PTY creation fails after install, rebuild the native module:
122
+
123
+ ```bash
124
+ npm rebuild node-pty --build-from-source
125
+ ```
126
+
127
+ ## Common Commands
128
+
129
+ ```bash
130
+ npm run onboarding
131
+ npm run service:status
132
+ npm run service:logs
133
+ npm run service:restart
134
+ npm test
135
+ npm run build
136
+ ```
137
+
138
+ For production-style local operation, `npm run ctl -- <command>` wraps `scripts/nterminalctl` and manages the built server, PID file, logs, health checks, and restarts.
139
+
140
+ ## Documentation
141
+
142
+ - [Onboarding](docs/onboarding.md): main/secondary setup, nginx, TLS, firewall, and local-only setup.
143
+ - [Configuration](docs/configuration.md): `.env`, environment variables, and deployment examples.
144
+ - [Operations](docs/operations.md): service control, updates, clean uninstall, verification, and troubleshooting.
145
+ - [Terminal History](docs/terminal-history.md): tmux scrollback, alternate buffer behavior, Codex/Claude JSONL history, and large-output ergonomics.
146
+ - [Features](docs/features.md): files, uploads, notifications, mobile controls, keyboard shortcuts, and PWA behavior.
147
+ - [Security](SECURITY.md): supported security boundary and vulnerability reporting.
148
+
149
+ ## Limitations
150
+
151
+ - No multi-user accounts or roles.
152
+ - No container isolation for spawned shells by default.
153
+ - No recovery of live PTYs after an OS reboot.
154
+ - No Node-restart PTY persistence on hosts without `tmux`.
155
+ - No VS Code extension host, debugger, task runner, or Git UI.
156
+ - No package-manager distribution is published yet.
157
+
158
+ ## Publication Notes
159
+
160
+ Keep local runtime files out of source control:
161
+
162
+ - `.env`
163
+ - `.env.*` except `.env.example`
164
+ - `.nterminal/`
165
+ - `.claude/`
166
+ - `dist/`
167
+ - logs
168
+ - `test-results/`
169
+ - `playwright-report/`
170
+
171
+ The package metadata is prepared for npm publication. The source code is licensed under GPL-3.0-only.
172
+
173
+ ## Acknowledgements
174
+
175
+ NTerminal is inspired by [Mansio](https://github.com/Younkyum/mansio) and [Leominal](https://github.com/jcwleo/leominal).
176
+
177
+ It reworks the persistent browser terminal idea around multi-server operation, OTP and IP-bound sessions, easier onboarding, and a more browser-native terminal experience with smooth scrollback and tab management, aiming to provide a more convenient and safer workspace for developers working across multiple remote servers.
178
+
179
+ ## License
180
+
181
+ NTerminal is licensed under GPL-3.0-only. See [LICENSE](LICENSE).
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
2
+ <rect width="64" height="64" rx="12" ry="12" fill="#0A0A0B"/>
3
+ <text font-family="'JetBrains Mono', ui-monospace, monospace" font-weight="700" font-size="50" dominant-baseline="middle"><tspan x="-13.00" y="34.56" fill="#FF2A3D">{</tspan><tspan x="17.00" y="34.56" fill="#fff">n</tspan><tspan x="47.00" y="34.56" fill="#FF2A3D">}</tspan></text>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
2
+ <rect width="32" height="32" rx="6" ry="6" fill="#0A0A0B"/>
3
+ <text font-family="'JetBrains Mono', ui-monospace, monospace" font-weight="700" font-size="25" dominant-baseline="middle"><tspan x="-6.50" y="17.28" fill="#FF2A3D">{</tspan><tspan x="8.50" y="17.28" fill="#fff">n</tspan><tspan x="23.50" y="17.28" fill="#FF2A3D">}</tspan></text>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 243 34" width="243" height="34">
2
+ <text font-family="'JetBrains Mono', ui-monospace, monospace" font-weight="700" font-size="33"><tspan x="0.00" y="28.00" fill="#FF2A3D">{</tspan><tspan x="22.16" y="28.00" fill="#fff">n</tspan><tspan x="41.96" y="28.00" fill="#FF2A3D">·</tspan><tspan x="61.76" y="28.00" fill="#fff">terminal</tspan><tspan x="222.51" y="28.00" fill="#FF2A3D">}</tspan></text>
3
+ </svg>
Binary file
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const binDir = path.dirname(fileURLToPath(import.meta.url));
9
+ const appDir = path.resolve(binDir, '..');
10
+ const packageMode = !existsSync(path.join(appDir, '.git')) && !existsSync(path.join(appDir, 'src'));
11
+ const userStateDir = path.join(os.homedir(), '.nterminal');
12
+
13
+ const [command = 'help', ...args] = process.argv.slice(2);
14
+
15
+ function defaultEnv() {
16
+ const env = {
17
+ ...process.env,
18
+ NTERMINAL_APP_DIR: process.env.NTERMINAL_APP_DIR ?? appDir
19
+ };
20
+ if (packageMode) {
21
+ env.NTERMINAL_ENV_FILE ??= path.join(userStateDir, '.env');
22
+ env.NTERMINAL_STATE_PATH ??= path.join(userStateDir, 'state.json');
23
+ env.NTERMINAL_PID_PATH ??= path.join(userStateDir, 'nterminal.pid');
24
+ env.NTERMINAL_LOG_PATH ??= path.join(userStateDir, 'nterminal.log');
25
+ }
26
+ return env;
27
+ }
28
+
29
+ function run(executable, runArgs) {
30
+ const result = spawnSync(executable, runArgs, {
31
+ cwd: appDir,
32
+ stdio: 'inherit',
33
+ env: defaultEnv()
34
+ });
35
+ process.exit(result.status ?? 1);
36
+ }
37
+
38
+ function runNode(modulePath, runArgs) {
39
+ run(process.execPath, [modulePath, ...runArgs]);
40
+ }
41
+
42
+ function runTypeScriptScript(scriptPath, runArgs) {
43
+ run(process.execPath, ['--import', 'tsx', scriptPath, ...runArgs]);
44
+ }
45
+
46
+ function scriptEntrypoint(name) {
47
+ const built = path.join(appDir, 'dist', 'scripts', `${name}.js`);
48
+ if (existsSync(built)) {
49
+ return { type: 'node', path: built };
50
+ }
51
+ const source = path.join(appDir, 'scripts', `${name}.ts`);
52
+ if (existsSync(source)) {
53
+ return { type: 'tsx', path: source };
54
+ }
55
+ return null;
56
+ }
57
+
58
+ function runScript(name, runArgs) {
59
+ const entrypoint = scriptEntrypoint(name);
60
+ if (!entrypoint) {
61
+ console.error(`error: ${name} entrypoint not found; run npm run build first`);
62
+ process.exit(1);
63
+ }
64
+ if (entrypoint.type === 'node') {
65
+ runNode(entrypoint.path, runArgs);
66
+ }
67
+ runTypeScriptScript(entrypoint.path, runArgs);
68
+ }
69
+
70
+ function runCtl(runArgs) {
71
+ run('bash', [path.join(appDir, 'scripts', 'nterminalctl'), ...runArgs]);
72
+ }
73
+
74
+ function printHelp() {
75
+ console.log(`Usage: nterminal <command>
76
+
77
+ Commands:
78
+ onboarding Run interactive main/secondary onboarding.
79
+ generate-secrets Print a new NTERMINAL_SESSION_SECRET.
80
+ status Show process status.
81
+ start Start the built server.
82
+ stop Stop the managed server.
83
+ restart Restart the managed server.
84
+ build Build the app.
85
+ deploy npm install/ci, typecheck, build, then restart.
86
+ clean --force Remove local runtime state.
87
+ logs [N] Show recent logs.
88
+ logs -f [N] Follow logs.
89
+ doctor Check local runtime readiness.
90
+ pid Print the running PID.
91
+ url Print the configured local URL.
92
+ help Show this help.
93
+ `);
94
+ }
95
+
96
+ switch (command) {
97
+ case 'onboarding':
98
+ runScript('onboarding', args);
99
+ break;
100
+ case 'generate-secrets':
101
+ case 'generate:secrets':
102
+ runScript('generate-secrets', args);
103
+ break;
104
+ case 'ctl':
105
+ runCtl(args);
106
+ break;
107
+ case 'help':
108
+ case '-h':
109
+ case '--help':
110
+ printHelp();
111
+ break;
112
+ default:
113
+ runCtl([command, ...args]);
114
+ }
Binary file