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
@@ -0,0 +1,97 @@
1
+ # Configuration
2
+
3
+ Configuration comes from `.env` or process environment variables.
4
+
5
+ ## `.env.example`
6
+
7
+ `.env.example` is the local runtime template. Keep it in source control, but never commit the copied `.env` file.
8
+
9
+ The template intentionally contains only the core values needed for a manual local start. Add advanced settings from the table below only when you need to override the defaults.
10
+
11
+ Source checkouts read `.env` from the repository root. npm package installs launched through `nterminal` read the runtime env file from `~/.nterminal/.env`.
12
+
13
+ For manual local setup:
14
+
15
+ ```bash
16
+ cp .env.example .env
17
+ npm run generate:secrets
18
+ ```
19
+
20
+ Paste the generated `NTERMINAL_SESSION_SECRET=...` value into `.env`.
21
+
22
+ For normal installs, prefer:
23
+
24
+ ```bash
25
+ npm run onboarding
26
+ ```
27
+
28
+ Onboarding writes `.env` for you.
29
+
30
+ ## Settings
31
+
32
+ | Setting | Default | Purpose |
33
+ | --- | --- | --- |
34
+ | `NTERMINAL_HOST` | `127.0.0.1` | Bind host. Keep the main server local unless a separate access boundary is in front. |
35
+ | `NTERMINAL_PORT` | `3107` | HTTP server port. |
36
+ | `NTERMINAL_WORKSPACE_ROOT` | launch directory | Startup directory for new terminal tabs. |
37
+ | `NTERMINAL_SHELL` | `$SHELL`, then `/bin/bash` | Shell path for new PTYs. |
38
+ | `NTERMINAL_STATE_PATH` | `.nterminal/state.json` | Local state file for password and layout data. |
39
+ | `NTERMINAL_SESSION_SECRET` | required | Server secret for password hashing and session cookies. |
40
+ | `NTERMINAL_SESSION_TTL_SECONDS` | `43200` | Session lifetime in seconds. |
41
+ | `NTERMINAL_COOKIE_SECURE` | `false` locally | Set `true` when serving through HTTPS. |
42
+ | `NTERMINAL_ALLOWED_ORIGINS` | local origins | Comma-separated browser origins allowed for mutating requests. |
43
+ | `NTERMINAL_TRUST_PROXY` | `false` | Trust `X-Forwarded-For` for session IP binding. Use only behind a reverse proxy you control. |
44
+ | `NTERMINAL_AGENT_TOKEN` | empty | Shared token used by the main server to reach a secondary server. Generated by secondary onboarding. |
45
+ | `NTERMINAL_UPLOAD_MAX_FILES` | `1024` | Maximum files accepted in one drag-and-drop upload. |
46
+ | `NTERMINAL_UPLOAD_MAX_FILE_BYTES` | `536870912` | Maximum bytes accepted for one uploaded file. |
47
+ | `NTERMINAL_UPLOAD_MAX_BATCH_BYTES` | `2147483648` | Maximum total bytes accepted for one upload batch. |
48
+ | `NTERMINAL_FILE_LIST_MAX_ENTRIES` | `2000` | Maximum entries returned in one file explorer directory listing. |
49
+ | `NTERMINAL_FILE_TEXT_MAX_BYTES` | `1048576` | Maximum bytes loaded for text or Markdown view/edit. |
50
+ | `NTERMINAL_FILE_PREVIEW_MAX_BYTES` | `52428800` | Maximum bytes served for browser file previews. |
51
+ | `NTERMINAL_PID_PATH` | `.nterminal/nterminal.pid` | PID file for the control script. |
52
+ | `NTERMINAL_LOG_PATH` | `.nterminal/nterminal.log` | Log file for the control script. |
53
+ | `NTERMINAL_HEALTH_TIMEOUT_SECONDS` | `15` | Startup health-check timeout for the control script. |
54
+ | `NTERMINAL_STOP_TIMEOUT_SECONDS` | `20` | Graceful stop timeout for the control script. |
55
+
56
+ ## Localhost Only
57
+
58
+ ```env
59
+ NTERMINAL_HOST=127.0.0.1
60
+ NTERMINAL_PORT=3107
61
+ NTERMINAL_ALLOWED_ORIGINS=http://127.0.0.1:3107,http://localhost:3107
62
+ NTERMINAL_COOKIE_SECURE=false
63
+ ```
64
+
65
+ ## Private VPN
66
+
67
+ Run NTerminal on the private interface or behind a proxy reachable only over VPN. Keep `NTERMINAL_ALLOWED_ORIGINS` aligned with the exact browser URL.
68
+
69
+ ```text
70
+ iPad or laptop
71
+ -> private VPN address
72
+ -> NTerminal or reverse proxy
73
+ -> host shell PTY
74
+ ```
75
+
76
+ ## HTTPS Reverse Proxy
77
+
78
+ Terminate HTTPS in a reverse proxy and forward to NTerminal on localhost:
79
+
80
+ ```text
81
+ Browser
82
+ -> https://terminal.example.internal
83
+ -> reverse proxy with access control
84
+ -> http://127.0.0.1:3107
85
+ ```
86
+
87
+ Use:
88
+
89
+ ```env
90
+ NTERMINAL_HOST=127.0.0.1
91
+ NTERMINAL_PORT=3107
92
+ NTERMINAL_COOKIE_SECURE=true
93
+ NTERMINAL_ALLOWED_ORIGINS=https://terminal.example.internal
94
+ NTERMINAL_TRUST_PROXY=true
95
+ ```
96
+
97
+ Only set `NTERMINAL_TRUST_PROXY` when the proxy is controlled by you and NTerminal is not directly reachable by clients that can spoof `X-Forwarded-For`.
@@ -0,0 +1,126 @@
1
+ # Features
2
+
3
+ ## Terminal Workspace
4
+
5
+ - Tabs and split panes for real host PTYs.
6
+ - Per-server workspace state.
7
+ - Refresh and temporary WebSocket reconnects reattach to active PTYs.
8
+ - Soft close detaches terminals into restorable pills.
9
+ - Tab rename, drag reorder, and drag-to-split controls.
10
+ - Activity dots and completion badges show whether a tab is running, idle, exited, or finished in the background.
11
+ - Detached terminal destroy uses a themed confirmation modal.
12
+ - Tab titles sync with tmux window names when tmux persistence is active.
13
+
14
+ ## Server Management
15
+
16
+ - One browser UI can manage the main server and multiple secondary servers.
17
+ - Each server keeps its own terminal layout, tabs, split panes, detached terminals, and file context.
18
+ - The sidebar switches servers without tearing down the inactive server workspaces.
19
+ - Settings includes an Updates section for checking git status and updating each server.
20
+ - Update all runs secondary server updates in parallel first, then updates the main server last.
21
+
22
+ ## Security And Access
23
+
24
+ - Password plus TOTP two-factor authentication.
25
+ - Single active session: a new login or re-auth revokes the previous browser session.
26
+ - Session IP binding rejects cookies presented from an unexpected client IP.
27
+ - Trusted devices can re-authenticate with a TOTP code only for 30 days.
28
+ - Trusted networks can be added by exact IP or CIDR to skip the re-auth prompt.
29
+ - Security settings can revoke trusted devices, remove trusted networks, and disable TOTP.
30
+
31
+ ## Terminal Appearance And Settings
32
+
33
+ - Settings is organized into Terminal, Shortcuts, Notifications, Security, and Updates sections.
34
+ - The settings sidebar highlights the visible section and jumps directly to that section.
35
+ - Terminal font size is adjustable with a slider, stepper, numeric input, and reset button.
36
+ - Terminal font family can switch between MesloLGS NF, JetBrains Mono, and system monospace stacks.
37
+ - Scroll progress indicators can be shown or hidden.
38
+ - Accent themes update UI accents and terminal selection colors, including Signal Red.
39
+
40
+ ## Terminal History And Scrollback
41
+
42
+ - Mouse wheel scrolling is captured for terminal output history instead of Codex/Claude prompt history.
43
+ - tmux-backed panes load older scrollback incrementally when the viewport reaches the top.
44
+ - Scroll progress indicators, jump-to-bottom controls, and follow-tail behavior keep large outputs usable.
45
+ - Supported Codex/Claude panes show a `JSONL` header indicator and can continue into archived transcript history.
46
+ - JSONL transcript history is loaded by cursor only when NTerminal can determine the session source unambiguously.
47
+
48
+ ## Files
49
+
50
+ The sidebar `files` tab browses the selected pane's current working directory. It is scoped to that directory tree and does not browse above the pane cwd.
51
+
52
+ Supported operations:
53
+
54
+ - directory listing
55
+ - text and Markdown view/edit
56
+ - image/PDF/browser-supported preview within configured limits
57
+ - create, rename, save, and delete with confirmation
58
+ - stale editor save rejection when the file changed after opening
59
+
60
+ The file explorer pins to the terminal that was active when Files opened. Switching pane focus does not reload it; the pinned pane cwd, server switch, or explicit Refresh button controls reloads.
61
+
62
+ ## Drag And Drop Uploads
63
+
64
+ Drop files or folders onto the terminal workspace to upload them into the active pane cwd.
65
+
66
+ - Folder drops keep the dropped folder name and preserve internal paths.
67
+ - Existing files are not overwritten; NTerminal writes a renamed copy such as `name 2.ext`.
68
+ - Upload progress and partial failures appear in the bottom-right terminal popup.
69
+ - Unsupported folder drops fail visibly instead of flattening the folder.
70
+
71
+ ## Completion Notifications
72
+
73
+ NTerminal can show completion notifications when a background command transitions from busy to idle.
74
+
75
+ Controls in Settings:
76
+
77
+ - toast duration
78
+ - persistent-until-dismissed mode
79
+ - system notifications while the page is hidden
80
+ - built-in notification video previews
81
+ - MP4/WebM upload up to 5 MB
82
+ - enable/disable videos with checkboxes
83
+ - random selection from enabled videos
84
+
85
+ Clicking a completion toast switches back to the server and pane that finished.
86
+
87
+ ## Compose Drawer And Clipboard
88
+
89
+ - Compose drawer keeps drafts per pane.
90
+ - Sending from compose writes carriage returns so multiline commands execute instead of only inserting visual newlines.
91
+ - Ctrl+C copies selected terminal text and falls through to SIGINT when nothing is selected.
92
+ - Ctrl+V pastes text through the browser Clipboard API.
93
+
94
+ ## Mobile Controls
95
+
96
+ - Mobile key bar is hidden by default and can expose Ctrl, Esc, Tab, and arrows.
97
+ - The bar floats above the soft keyboard where keyboard-inset detection is available.
98
+ - Touch-specific button states avoid sticky hover/focus artifacts.
99
+ - Scroll handles are easier to drag on mobile.
100
+
101
+ ## Keyboard Shortcuts
102
+
103
+ NTerminal uses Control-based workspace shortcuts so browser-reserved Command shortcuts stay available.
104
+
105
+ | Action | Shortcut |
106
+ | --- | --- |
107
+ | Previous / next server | `Ctrl+Up` / `Ctrl+Down` |
108
+ | Previous / next tab | `Ctrl+Left` / `Ctrl+Right` |
109
+ | Select tab by order | `Ctrl+Alt+1` through `Ctrl+Alt+9` |
110
+ | Select pane by screen order | `Ctrl+Alt+Shift+1` through `Ctrl+Alt+Shift+9` |
111
+ | Split right | `Ctrl+Alt+Shift+Right` |
112
+ | Split down | `Ctrl+Alt+Shift+Down` |
113
+
114
+ Shortcuts can be customized in Settings. If the browser, operating system, or focused editable control claims a key combination first, that handler wins before NTerminal can receive it.
115
+
116
+ ## iPad Home Screen
117
+
118
+ NTerminal includes PWA metadata and Apple icons:
119
+
120
+ - `public/apple-touch-icon.png`
121
+ - `public/icons/app-icon-384.png`
122
+ - `public/icons/app-icon-1024.png`
123
+ - `public/icons/favicon.svg`
124
+ - `public/manifest.webmanifest`
125
+
126
+ After opening NTerminal in Safari on iPad, use Share -> Add to Home Screen.
@@ -0,0 +1,122 @@
1
+ # Onboarding
2
+
3
+ NTerminal uses one interactive command for both the main server and secondary servers:
4
+
5
+ ```bash
6
+ npm run onboarding
7
+ ```
8
+
9
+ `npm install` runs automatically as the `preonboarding` hook, so a fresh checkout does not need a separate install step.
10
+
11
+ After npm publication, package installs use the CLI directly:
12
+
13
+ ```bash
14
+ npm install -g nterminal
15
+ nterminal onboarding
16
+ ```
17
+
18
+ CLI package installs store `.env`, state, pid, and log files under `~/.nterminal`. Install with a user-writable npm global prefix so in-app updates can run `npm install -g nterminal@latest` without sudo.
19
+
20
+ ## Main Server
21
+
22
+ Run onboarding on the machine that should serve the browser UI:
23
+
24
+ ```bash
25
+ npm run onboarding
26
+ ```
27
+
28
+ Choose `main`.
29
+
30
+ The main setup flow:
31
+
32
+ 1. Picks a bind address and port. The default main bind is `127.0.0.1`, so public traffic goes through the configured reverse proxy instead of bypassing TLS.
33
+ 2. Requires a public `https://<domain>` URL for the proxy setup path. Raw IPs, `http://`, `localhost`, and `.local` URLs are rejected in that path.
34
+ 3. Writes `.env`, including a generated `NTERMINAL_SESSION_SECRET`.
35
+ 4. Sets the login password and enables TOTP headlessly. The CLI prints a terminal QR code, base32 secret, and `otpauth://` URI, then verifies the 6-digit code.
36
+ 5. Optionally builds and starts the server with `scripts/nterminalctl deploy`.
37
+ 6. Optionally provisions nginx, DNS verification, firewall rules, and Let's Encrypt TLS.
38
+
39
+ The final summary prints the public URL, internal bind, and next steps.
40
+
41
+ ## Secondary Server
42
+
43
+ Run onboarding on each additional machine that should appear in the main UI:
44
+
45
+ ```bash
46
+ npm run onboarding
47
+ ```
48
+
49
+ Choose `secondary`.
50
+
51
+ The secondary setup flow:
52
+
53
+ 1. Binds to `0.0.0.0` and picks a free port, preferring `3107`.
54
+ 2. Asks for the URL the main server should use to reach this machine.
55
+ 3. Asks for a display name.
56
+ 4. Generates a session secret and `NTERMINAL_AGENT_TOKEN`, then writes `.env`.
57
+ 5. Prompts for the main server URL, password, and authenticator code.
58
+ 6. Registers this secondary server with the main server automatically.
59
+ 7. Optionally builds and starts the server.
60
+
61
+ Onboard the main server first. The main must already have password and TOTP configured before a secondary server can register itself.
62
+
63
+ Logging into the main server from the secondary CLI counts as a login, so the single-active-session policy may revoke an open browser session. Log in again in the browser afterward.
64
+
65
+ ## Proxy Setup
66
+
67
+ When main onboarding configures a public URL, it verifies and provisions:
68
+
69
+ - public IP detection through `1.1.1.1`
70
+ - DNS A record match
71
+ - nginx installation when missing
72
+ - nginx site config
73
+ - nginx reload and local proxy verification
74
+ - `ufw` or `firewalld` rules for HTTP/HTTPS
75
+ - `certbot --nginx` certificate issuance
76
+ - real HTTPS verification against the final domain
77
+
78
+ Privileged work uses one of two modes:
79
+
80
+ - **auto-sudo**: if `sudo -n true` works, onboarding runs the needed commands directly with sudo.
81
+ - **manual**: onboarding prints the exact command, explains it, waits, and re-verifies before continuing.
82
+
83
+ The "Main is ready" summary is printed only after the selected setup path has completed its checks.
84
+
85
+ ## Local-Only Start
86
+
87
+ For a localhost-only development run without nginx/TLS onboarding:
88
+
89
+ ```bash
90
+ npm install
91
+ cp .env.example .env
92
+ npm run generate:secrets
93
+ ```
94
+
95
+ Paste the generated value into `.env`:
96
+
97
+ ```env
98
+ NTERMINAL_SESSION_SECRET=replace-with-generate-secrets-output
99
+ ```
100
+
101
+ Then build and start:
102
+
103
+ ```bash
104
+ npm run build
105
+ npm start
106
+ ```
107
+
108
+ Open:
109
+
110
+ ```text
111
+ http://127.0.0.1:3107
112
+ ```
113
+
114
+ On the first visit, set the local NTerminal password and configure TOTP.
115
+
116
+ ## Re-Running Onboarding
117
+
118
+ You can re-run onboarding to refresh generated config or register a secondary server again.
119
+
120
+ Existing `NTERMINAL_HOST`, `NTERMINAL_PORT`, `NTERMINAL_SESSION_SECRET`, and `NTERMINAL_AGENT_TOKEN` values are preserved on re-run, so onboarding does not silently change the bind address, kick live sessions, or rotate a secondary server identity.
121
+
122
+ Registering the same secondary server URL again replaces the previous entry for that URL.
@@ -0,0 +1,112 @@
1
+ # Operations
2
+
3
+ ## Local Service Management
4
+
5
+ For production-style local operation, use the bundled control script through npm.
6
+
7
+ ```bash
8
+ npm run ctl -- doctor
9
+ npm run ctl -- build
10
+ npm run ctl -- start
11
+ npm run ctl -- status
12
+ npm run ctl -- logs
13
+ npm run ctl -- restart
14
+ npm run ctl -- stop
15
+ ```
16
+
17
+ The script starts the built server in the background, writes a PID file, appends logs, waits for the local health endpoint, and stops the real Node PID so PTY cleanup can run.
18
+
19
+ Source checkouts keep runtime files in the repository's `.nterminal/` directory by default. npm package installs launched through `nterminal` keep runtime files under `~/.nterminal`.
20
+
21
+ One-command local deploy:
22
+
23
+ ```bash
24
+ npm run deploy:local
25
+ ```
26
+
27
+ Short aliases:
28
+
29
+ ```bash
30
+ npm run service:status
31
+ npm run service:start
32
+ npm run service:stop
33
+ npm run service:restart
34
+ npm run service:logs
35
+ ```
36
+
37
+ ## Updates
38
+
39
+ Each server updates itself with the method used to install it.
40
+
41
+ The **updates** panel in the main server sidebar footer can manage the fleet:
42
+
43
+ - **Check for updates** runs `git fetch` for git checkouts, or checks the npm registry for package installs.
44
+ - **Update** runs `git pull --ff-only` plus rebuild/restart for git checkouts, or `npm install -g nterminal@latest` plus restart for package installs.
45
+ - **Update all** updates secondary servers in parallel first and the main server last.
46
+
47
+ Notes:
48
+
49
+ - `--ff-only` stops and reports an error when a checkout has local commits or a diverged branch.
50
+ - npm package updates run as the same user that owns the running server process. Use a user-writable npm prefix, not a root-owned global install, if you want UI updates to work without sudo.
51
+ - A git checkout with no upstream branch, or an npm registry/permission error, is reported as an explanatory status.
52
+ - Update endpoints require the same auth boundary as the rest of NTerminal.
53
+
54
+ ## Clean Local State
55
+
56
+ To clean a local install before reinstalling:
57
+
58
+ ```bash
59
+ scripts/nterminalctl clean --force --env
60
+ ```
61
+
62
+ This stops the managed server, kills NTerminal tmux sessions, removes `.nterminal/`, and removes `.env`.
63
+
64
+ Options:
65
+
66
+ - omit `--env` to keep `.env`
67
+ - add `--keep-tmux` to preserve live tmux-backed panes
68
+
69
+ ## Verification
70
+
71
+ Run the standard checks before publishing or deploying:
72
+
73
+ ```bash
74
+ npm run typecheck
75
+ npm test
76
+ npm run build
77
+ git diff --check
78
+ ```
79
+
80
+ For a full browser smoke test:
81
+
82
+ ```bash
83
+ npm run test:e2e
84
+ ```
85
+
86
+ Manual smoke test:
87
+
88
+ 1. Start the built server.
89
+ 2. Set the initial password in the browser.
90
+ 3. Create a terminal.
91
+ 4. Run `printf nterminal-ok`.
92
+ 5. Split the terminal.
93
+ 6. Refresh the browser and confirm the terminal reconnects.
94
+ 7. Close a pane and confirm it detaches as a pill.
95
+ 8. Destroy the pill and confirm the PTY exits.
96
+
97
+ ## Troubleshooting
98
+
99
+ If native PTY install or startup fails:
100
+
101
+ ```bash
102
+ npm rebuild node-pty --build-from-source
103
+ ```
104
+
105
+ If the service script will not start:
106
+
107
+ ```bash
108
+ npm run ctl -- doctor
109
+ npm run ctl -- logs
110
+ ```
111
+
112
+ If you lose the local NTerminal password, stop the server and remove the local state file at `NTERMINAL_STATE_PATH`. The next browser visit will show the first-run password setup screen again.
@@ -0,0 +1,54 @@
1
+ # Terminal History
2
+
3
+ NTerminal is tuned for terminal-first work in TUIs such as Codex and Claude, where mouse wheel behavior and long output history matter.
4
+
5
+ ## Mouse Wheel Behavior
6
+
7
+ Wheel events are captured at the pane level and applied to xterm scrollback. This keeps the wheel focused on terminal output history instead of Codex/Claude prompt history.
8
+
9
+ If a pane is in the alternate screen, NTerminal exits the alternate buffer before scrolling so the scrollback can be shown.
10
+
11
+ ## Incremental tmux Scrollback
12
+
13
+ When tmux-backed persistence is available, NTerminal loads older scrollback on demand:
14
+
15
+ - older lines are requested only when the viewport reaches the top
16
+ - each page is capped to avoid large one-shot payloads
17
+ - a spinner appears while older history is loading
18
+ - the visible position is preserved when lines are prepended
19
+
20
+ This avoids sending an entire long terminal history to the browser during tab or server switches.
21
+
22
+ ## Scroll Progress And Bottom Controls
23
+
24
+ Panes can show:
25
+
26
+ - a faint `current/total` progress indicator before scrolling
27
+ - a stronger progress indicator while actively scrolling
28
+ - a bottom-right jump-to-bottom button when the operator is away from the tail
29
+ - larger mobile drag handles for touch scrolling
30
+
31
+ Live output follows the tail unless the operator intentionally scrolls away.
32
+
33
+ ## Codex / Claude JSONL History
34
+
35
+ After tmux scrollback is exhausted, supported Codex and Claude panes can continue into archived JSONL transcript history.
36
+
37
+ Supported panes show a `JSONL` indicator in the pane header.
38
+
39
+ Sources:
40
+
41
+ - Codex: `~/.codex/sessions`
42
+ - Claude: `~/.claude/projects`
43
+
44
+ NTerminal attaches JSONL history only when it can determine the session source unambiguously. It does not guess by fuzzy scoring.
45
+
46
+ JSONL records are loaded by cursor and rendered into readable user, assistant, and tool blocks. NTerminal does not send the full archive to the browser at once.
47
+
48
+ ## Persistence Boundaries
49
+
50
+ - Refreshing the browser or temporarily losing the WebSocket should reconnect to active PTYs while the Node process is still running.
51
+ - Restarting the NTerminal Node server preserves active shells when `tmux` is installed and `NTERMINAL_PTY_PERSIST` is not disabled.
52
+ - Without tmux, Node restarts kill direct PTYs.
53
+ - An OS reboot terminates NTerminal, tmux, and all live shells.
54
+ - Codex/Claude JSONL transcript files are managed by those tools and are not stored in NTerminal state.
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "nterminal",
3
+ "version": "1.2.0",
4
+ "license": "GPL-3.0-only",
5
+ "type": "module",
6
+ "packageManager": "npm@10.9.7",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "registry": "https://registry.npmjs.org/"
10
+ },
11
+ "bin": {
12
+ "nterminal": "bin/nterminal.js"
13
+ },
14
+ "files": [
15
+ ".env.example",
16
+ "LICENSE",
17
+ "README.md",
18
+ "assets/",
19
+ "bin/",
20
+ "dist/",
21
+ "docs/",
22
+ "public/",
23
+ "scripts/nterminalctl"
24
+ ],
25
+ "scripts": {
26
+ "dev": "tsx watch src/server/index.ts",
27
+ "build": "rm -rf dist/server dist/shared dist/scripts dist/src && tsc -p tsconfig.server.json && tsc -p tsconfig.scripts.json && vite build",
28
+ "start": "node dist/server/index.js",
29
+ "typecheck": "tsc -p tsconfig.server.json --noEmit && tsc -p tsconfig.client.json --noEmit",
30
+ "test": "vitest run",
31
+ "test:e2e": "npm run build && playwright test",
32
+ "generate:secrets": "tsx scripts/generate-secrets.ts",
33
+ "preonboarding": "npm install",
34
+ "onboarding": "node bin/nterminal.js onboarding",
35
+ "prepack": "npm run build",
36
+ "prepublishOnly": "npm run pack:check",
37
+ "pack:check": "node scripts/check-npm-package.js",
38
+ "ctl": "bash scripts/nterminalctl",
39
+ "deploy:local": "bash scripts/nterminalctl deploy",
40
+ "service:status": "bash scripts/nterminalctl status",
41
+ "service:start": "bash scripts/nterminalctl start",
42
+ "service:stop": "bash scripts/nterminalctl stop",
43
+ "service:restart": "bash scripts/nterminalctl restart",
44
+ "service:logs": "bash scripts/nterminalctl logs"
45
+ },
46
+ "engines": {
47
+ "node": ">=22"
48
+ },
49
+ "dependencies": {
50
+ "@dnd-kit/core": "^6.3.1",
51
+ "@dnd-kit/utilities": "^3.2.2",
52
+ "@fastify/cookie": "^11.0.2",
53
+ "@fastify/multipart": "^10.0.0",
54
+ "@fastify/static": "^9.1.3",
55
+ "@fastify/websocket": "^11.2.0",
56
+ "@xterm/addon-fit": "^0.11.0",
57
+ "@xterm/xterm": "^6.0.0",
58
+ "fastify": "^5.6.2",
59
+ "ipaddr.js": "^2.4.0",
60
+ "node-pty": "^1.1.0",
61
+ "otpauth": "^9.5.1",
62
+ "qrcode": "^1.5.4",
63
+ "react": "^19.2.0",
64
+ "react-dom": "^19.2.0",
65
+ "react-markdown": "^10.1.0",
66
+ "remark-gfm": "^4.0.1",
67
+ "ws": "^8.18.3"
68
+ },
69
+ "devDependencies": {
70
+ "@playwright/test": "^1.57.0",
71
+ "@testing-library/jest-dom": "^6.9.1",
72
+ "@testing-library/react": "^16.3.0",
73
+ "@types/node": "^24.10.1",
74
+ "@types/qrcode": "^1.5.6",
75
+ "@types/react": "^19.2.6",
76
+ "@types/react-dom": "^19.2.3",
77
+ "@types/ws": "^8.18.1",
78
+ "@vitejs/plugin-react": "^5.1.1",
79
+ "jsdom": "^27.2.0",
80
+ "tsx": "^4.20.6",
81
+ "typescript": "^5.9.3",
82
+ "vite": "^7.2.4",
83
+ "vitest": "^4.0.14"
84
+ }
85
+ }
Binary file
@@ -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
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,24 @@
1
+ {
2
+ "name": "NTerminal",
3
+ "short_name": "NTerminal",
4
+ "description": "A touch-friendly terminal workspace.",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "background_color": "#071113",
9
+ "theme_color": "#071113",
10
+ "icons": [
11
+ {
12
+ "src": "/icons/app-icon-384.png",
13
+ "sizes": "384x384",
14
+ "type": "image/png",
15
+ "purpose": "any"
16
+ },
17
+ {
18
+ "src": "/icons/app-icon-1024.png",
19
+ "sizes": "1024x1024",
20
+ "type": "image/png",
21
+ "purpose": "any maskable"
22
+ }
23
+ ]
24
+ }