nterminal 1.2.12 → 1.2.14

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 (38) hide show
  1. package/README.md +9 -36
  2. package/bin/nterminal.js +10 -23
  3. package/dist/client/assets/{MarkdownPreview-DfZZD0C0.js → MarkdownPreview-DBMjXHXe.js} +1 -1
  4. package/dist/client/assets/index-BprWaATf.css +1 -0
  5. package/dist/client/assets/index-DJRitqb8.js +44 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/scripts/onboarding.js +31 -15
  8. package/dist/scripts/onboarding.js.map +1 -1
  9. package/dist/scripts/proxySetup.js +4 -1
  10. package/dist/scripts/proxySetup.js.map +1 -1
  11. package/dist/server/agent/agentRoutes.js +4 -2
  12. package/dist/server/agent/agentRoutes.js.map +1 -1
  13. package/dist/server/auth/cookies.js +0 -8
  14. package/dist/server/auth/cookies.js.map +1 -1
  15. package/dist/server/config.js +1 -1
  16. package/dist/server/config.js.map +1 -1
  17. package/dist/server/routes/agentManagementRoutes.js +87 -13
  18. package/dist/server/routes/agentManagementRoutes.js.map +1 -1
  19. package/dist/server/routes/updateRoutes.js +2 -2
  20. package/dist/server/routes/updateRoutes.js.map +1 -1
  21. package/dist/server/update/{gitUpdate.d.ts → packageUpdate.d.ts} +1 -9
  22. package/dist/server/update/packageUpdate.js +140 -0
  23. package/dist/server/update/packageUpdate.js.map +1 -0
  24. package/dist/server/update/runtimeUninstall.d.ts +2 -0
  25. package/dist/server/update/runtimeUninstall.js +45 -0
  26. package/dist/server/update/runtimeUninstall.js.map +1 -0
  27. package/dist/shared/protocol.d.ts +6 -8
  28. package/dist/shared/protocol.js.map +1 -1
  29. package/docs/configuration.md +7 -24
  30. package/docs/features.md +1 -1
  31. package/docs/onboarding.md +5 -46
  32. package/docs/operations.md +26 -45
  33. package/package.json +1 -1
  34. package/scripts/nterminalctl +93 -57
  35. package/dist/client/assets/index-CMVGLebq.js +0 -43
  36. package/dist/client/assets/index-DiQ82H7w.css +0 -1
  37. package/dist/server/update/gitUpdate.js +0 -241
  38. package/dist/server/update/gitUpdate.js.map +0 -1
@@ -60,6 +60,12 @@ export interface AgentPingResponse {
60
60
  ok: boolean;
61
61
  latencyMs: number;
62
62
  }
63
+ export interface AgentUninstallResponse {
64
+ ok: boolean;
65
+ message: string;
66
+ disconnecting: boolean;
67
+ disconnected?: boolean;
68
+ }
63
69
  export interface SystemStatsResponse {
64
70
  cpuPercent: number | null;
65
71
  memoryPercent: number;
@@ -76,16 +82,8 @@ export interface SystemStatsResponse {
76
82
  export interface VersionInfo {
77
83
  version: string;
78
84
  runningVersion: string;
79
- commit: string | null;
80
- runningCommit: string | null;
81
- branch: string | null;
82
- dirty: boolean;
83
- isGit: boolean;
84
- installMode: 'git' | 'package';
85
85
  }
86
86
  export interface UpdateCheckResponse {
87
- behind: number | null;
88
- commit: string | null;
89
87
  currentVersion?: string;
90
88
  latestVersion?: string | null;
91
89
  updateAvailable?: boolean | null;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"AAyVA,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,MAAwC,CAAC;QACzD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3G,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,aAAa,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YAC7E,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,IACE,OAAO,CAAC,IAAI,KAAK,QAAQ;YACzB,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ;YACtC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;YAC9B,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAC9B,CAAC;YACD,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACjE,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"AAwVA,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,OAAO,GAAG,MAAwC,CAAC;QACzD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3G,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,aAAa,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YAC7E,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,IACE,OAAO,CAAC,IAAI,KAAK,QAAQ;YACzB,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ;YACtC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;YAC9B,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAC9B,CAAC;YACD,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACjE,OAAO,OAAgC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -4,34 +4,17 @@ Configuration comes from `.env` or process environment variables.
4
4
 
5
5
  ## `.env.example`
6
6
 
7
- `.env.example` is the local runtime template. Keep it in source control, but never commit the copied `.env` file.
7
+ `.env.example` is the runtime template. Keep it in source control, but never commit the copied `.env` file.
8
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.
9
+ The template intentionally contains only the core values. Add advanced settings from the table below only when you need to override the defaults.
10
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 package installs, prefer:
11
+ NTerminal package installs launched through `nterminal` read the runtime env file from `~/.nterminal/.env`.
23
12
 
24
13
  ```bash
25
14
  nterminal onboarding
26
15
  ```
27
16
 
28
- For source checkouts:
29
-
30
- ```bash
31
- npm run onboarding
32
- ```
33
-
34
- Onboarding writes `.env` for you.
17
+ Onboarding writes `.env` for you, including an absolute `NTERMINAL_STATE_PATH` so password/TOTP state is read from the same file when the service starts.
35
18
 
36
19
  ## Settings
37
20
 
@@ -41,7 +24,7 @@ Onboarding writes `.env` for you.
41
24
  | `NTERMINAL_PORT` | `3107` | HTTP server port. |
42
25
  | `NTERMINAL_WORKSPACE_ROOT` | home directory | Startup directory for new terminal tabs. |
43
26
  | `NTERMINAL_SHELL` | `$SHELL`, then `/bin/bash` | Shell path for new PTYs. |
44
- | `NTERMINAL_STATE_PATH` | `.nterminal/state.json` | Local state file for password and layout data. |
27
+ | `NTERMINAL_STATE_PATH` | `~/.nterminal/state.json` | Local state file for password and layout data. Onboarding writes an absolute path. |
45
28
  | `NTERMINAL_SESSION_SECRET` | required | Server secret for password hashing and session cookies. |
46
29
  | `NTERMINAL_SESSION_TTL_SECONDS` | `43200` | Session lifetime in seconds. |
47
30
  | `NTERMINAL_COOKIE_SECURE` | `false` locally | Set `true` when serving through HTTPS. |
@@ -54,8 +37,8 @@ Onboarding writes `.env` for you.
54
37
  | `NTERMINAL_FILE_LIST_MAX_ENTRIES` | `2000` | Maximum entries returned in one file explorer directory listing. |
55
38
  | `NTERMINAL_FILE_TEXT_MAX_BYTES` | `1048576` | Maximum bytes loaded for text or Markdown view/edit. |
56
39
  | `NTERMINAL_FILE_PREVIEW_MAX_BYTES` | `52428800` | Maximum bytes served for browser file previews. |
57
- | `NTERMINAL_PID_PATH` | `.nterminal/nterminal.pid` | PID file for the control script. |
58
- | `NTERMINAL_LOG_PATH` | `.nterminal/nterminal.log` | Log file for the control script. |
40
+ | `NTERMINAL_PID_PATH` | `~/.nterminal/nterminal.pid` | PID file for the control script. |
41
+ | `NTERMINAL_LOG_PATH` | `~/.nterminal/nterminal.log` | Log file for the control script. |
59
42
  | `NTERMINAL_HEALTH_TIMEOUT_SECONDS` | `15` | Startup health-check timeout for the control script. |
60
43
  | `NTERMINAL_STOP_TIMEOUT_SECONDS` | `20` | Graceful stop timeout for the control script. |
61
44
 
package/docs/features.md CHANGED
@@ -17,7 +17,7 @@
17
17
  - Each server keeps its own terminal layout, tabs, split panes, detached terminals, and file context.
18
18
  - The sidebar switches servers without tearing down the inactive server workspaces.
19
19
  - Server cards show CPU, memory, disk, and swap usage with percentages and absolute values.
20
- - Settings includes an Updates section for checking git status and updating each server.
20
+ - Settings includes an Updates section for checking npm package updates on each server.
21
21
  - Update all runs secondary server updates in parallel first, then updates the main server last.
22
22
  - Background update checks run hourly and badge Settings/Updates when an update is available.
23
23
 
@@ -2,22 +2,12 @@
2
2
 
3
3
  NTerminal uses one interactive command for both the main server and secondary servers.
4
4
 
5
- For npm package installs:
6
-
7
5
  ```bash
8
6
  npm install -g nterminal
9
7
  nterminal onboarding
10
8
  ```
11
9
 
12
- 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.
13
-
14
- For source checkouts:
15
-
16
- ```bash
17
- npm run onboarding
18
- ```
19
-
20
- `npm install` runs automatically as the `preonboarding` hook, so a fresh checkout does not need a separate install step.
10
+ NTerminal stores `.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.
21
11
 
22
12
  ## Main Server
23
13
 
@@ -33,9 +23,9 @@ The main setup flow:
33
23
 
34
24
  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.
35
25
  2. Requires a public `https://<domain>` URL for the proxy setup path. Raw IPs, `http://`, `localhost`, and `.local` URLs are rejected in that path.
36
- 3. Writes `.env`, including a generated `NTERMINAL_SESSION_SECRET`.
26
+ 3. Writes `.env`, including a generated `NTERMINAL_SESSION_SECRET` and absolute `NTERMINAL_STATE_PATH`.
37
27
  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.
38
- 5. Optionally starts the bundled package build, or builds and starts a source checkout, with `scripts/nterminalctl deploy`.
28
+ 5. Optionally starts the installed package with `nterminal deploy`.
39
29
  6. Optionally provisions nginx, DNS verification, firewall rules, and Let's Encrypt TLS.
40
30
 
41
31
  The final summary prints the public URL, internal bind, and next steps.
@@ -55,10 +45,10 @@ The secondary setup flow:
55
45
  1. Binds to `0.0.0.0` and picks a free port, preferring `3107`.
56
46
  2. Asks for the URL the main server should use to reach this machine.
57
47
  3. Asks for a display name.
58
- 4. Generates a session secret and `NTERMINAL_AGENT_TOKEN`, then writes `.env`.
48
+ 4. Generates a session secret and `NTERMINAL_AGENT_TOKEN`, then writes `.env` with an absolute state path.
59
49
  5. Prompts for the main server URL, password, and authenticator code.
60
50
  6. Registers this secondary server with the main server automatically.
61
- 7. Optionally starts the bundled package build, or builds and starts a source checkout.
51
+ 7. Optionally starts the installed package.
62
52
 
63
53
  Onboard the main server first. The main must already have password and TOTP configured before a secondary server can register itself.
64
54
 
@@ -84,37 +74,6 @@ Privileged work uses one of two modes:
84
74
 
85
75
  The "Main is ready" summary is printed only after the selected setup path has completed its checks.
86
76
 
87
- ## Local-Only Start
88
-
89
- For a localhost-only development run without nginx/TLS onboarding:
90
-
91
- ```bash
92
- npm install
93
- cp .env.example .env
94
- npm run generate:secrets
95
- ```
96
-
97
- Paste the generated value into `.env`:
98
-
99
- ```env
100
- NTERMINAL_SESSION_SECRET=replace-with-generate-secrets-output
101
- ```
102
-
103
- Then build and start:
104
-
105
- ```bash
106
- npm run build
107
- npm start
108
- ```
109
-
110
- Open:
111
-
112
- ```text
113
- http://127.0.0.1:3107
114
- ```
115
-
116
- On the first visit, set the local NTerminal password and configure TOTP.
117
-
118
77
  ## Re-Running Onboarding
119
78
 
120
79
  You can re-run onboarding to refresh generated config or register a secondary server again.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Local Service Management
4
4
 
5
- For production-style local operation from an npm package install, use the `nterminal` command:
5
+ Use the `nterminal` command:
6
6
 
7
7
  ```bash
8
8
  nterminal doctor
@@ -13,69 +13,56 @@ nterminal restart
13
13
  nterminal stop
14
14
  ```
15
15
 
16
- For source checkouts, use the bundled control script through npm:
17
-
18
- ```bash
19
- npm run ctl -- doctor
20
- npm run ctl -- build
21
- npm run ctl -- start
22
- npm run ctl -- status
23
- npm run ctl -- logs
24
- npm run ctl -- restart
25
- npm run ctl -- stop
26
- ```
27
-
28
16
  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.
29
17
 
30
- Source checkouts keep runtime files in the repository's `.nterminal/` directory by default. npm package installs launched through `nterminal` keep runtime files under `~/.nterminal`.
31
-
32
- One-command source checkout deploy:
33
-
34
- ```bash
35
- npm run deploy:local
36
- ```
18
+ Runtime files are kept under `~/.nterminal`.
37
19
 
38
20
  Short aliases:
39
21
 
40
22
  ```bash
41
- npm run service:status
42
- npm run service:start
43
- npm run service:stop
44
- npm run service:restart
45
- npm run service:logs
23
+ nterminal status
24
+ nterminal start
25
+ nterminal stop
26
+ nterminal restart
27
+ nterminal logs
46
28
  ```
47
29
 
48
30
  ## Updates
49
31
 
50
- Each server updates itself with the method used to install it.
32
+ Each server updates itself as an npm package install.
51
33
 
52
34
  The **updates** panel in the main server sidebar footer can manage the fleet:
53
35
 
54
- - **Check for updates** runs `git fetch` for git checkouts, or checks the npm registry for package installs.
55
- - **Update** runs `git pull --ff-only` plus rebuild/restart for git checkouts, or `npm install -g nterminal@latest` plus restart for package installs.
36
+ - **Check for updates** checks the npm registry.
37
+ - **Update** runs `npm install -g nterminal@latest` plus restart.
56
38
  - **Update all** updates secondary servers in parallel first and the main server last.
57
39
 
58
40
  Notes:
59
41
 
60
- - `--ff-only` stops and reports an error when a checkout has local commits or a diverged branch.
61
42
  - 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.
62
- - A git checkout with no upstream branch, or an npm registry/permission error, is reported as an explanatory status.
43
+ - An npm registry or permission error is reported as an explanatory status.
63
44
  - Update endpoints require the same auth boundary as the rest of NTerminal.
64
45
 
65
- ## Clean Local State
46
+ ## Clean Uninstall
66
47
 
67
- To clean a local install before reinstalling:
48
+ To remove the local runtime before uninstalling the npm package:
68
49
 
69
50
  ```bash
70
- nterminal clean --force --env
51
+ nterminal uninstall --force
52
+ npm uninstall -g nterminal
71
53
  ```
72
54
 
73
- This stops the managed server, kills NTerminal tmux sessions, removes `.nterminal/`, and removes `.env`.
74
- In a source checkout, run the same command as `npm run ctl -- clean --force --env`.
55
+ `npm uninstall -g nterminal` cannot run cleanup automatically on modern npm versions. Run `nterminal uninstall --force` first; it stops the managed server, unloads an NTerminal launchd agent when present, kills NTerminal tmux sessions, removes runtime state under `~/.nterminal`, and removes `~/.nterminal/.env`.
56
+
57
+ If you already removed the package while a server is still running, reinstall or use npx to run the cleanup command, then uninstall again:
58
+
59
+ ```bash
60
+ npx nterminal@latest uninstall --force
61
+ npm uninstall -g nterminal
62
+ ```
75
63
 
76
- Options:
64
+ Option:
77
65
 
78
- - omit `--env` to keep `.env`
79
66
  - add `--keep-tmux` to preserve live tmux-backed panes
80
67
 
81
68
  ## Verification
@@ -114,17 +101,11 @@ If native PTY install or startup fails for a package install:
114
101
  npm explore -g nterminal -- npm rebuild node-pty --build-from-source
115
102
  ```
116
103
 
117
- For source checkouts:
118
-
119
- ```bash
120
- npm rebuild node-pty --build-from-source
121
- ```
122
-
123
104
  If the service script will not start:
124
105
 
125
106
  ```bash
126
- npm run ctl -- doctor
127
- npm run ctl -- logs
107
+ nterminal doctor
108
+ nterminal logs
128
109
  ```
129
110
 
130
111
  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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nterminal",
3
- "version": "1.2.12",
3
+ "version": "1.2.14",
4
4
  "license": "GPL-3.0-only",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.7",
@@ -4,7 +4,8 @@ set -Eeuo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  DEFAULT_APP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
6
6
  APP_DIR="${NTERMINAL_APP_DIR:-$DEFAULT_APP_DIR}"
7
- ENV_FILE="${NTERMINAL_ENV_FILE:-$APP_DIR/.env}"
7
+ DEFAULT_RUNTIME_DIR="${HOME:-$APP_DIR}/.nterminal"
8
+ ENV_FILE="${NTERMINAL_ENV_FILE:-$DEFAULT_RUNTIME_DIR/.env}"
8
9
 
9
10
  COMMAND="${1:-help}"
10
11
  if [[ $# -gt 0 ]]; then
@@ -65,13 +66,17 @@ resolve_path() {
65
66
 
66
67
  load_env_file
67
68
 
69
+ DEFAULT_STATE_PATH="$DEFAULT_RUNTIME_DIR/state.json"
70
+ DEFAULT_PID_PATH="$DEFAULT_RUNTIME_DIR/nterminal.pid"
71
+ DEFAULT_LOG_PATH="$DEFAULT_RUNTIME_DIR/nterminal.log"
72
+
68
73
  HOST="${NTERMINAL_HOST:-127.0.0.1}"
69
74
  PORT="${NTERMINAL_PORT:-3107}"
70
75
  NODE_BIN="${NTERMINAL_NODE_BIN:-node}"
71
76
  NPM_BIN="${NTERMINAL_NPM_BIN:-npm}"
72
- PID_PATH="$(resolve_path "${NTERMINAL_PID_PATH:-.nterminal/nterminal.pid}")"
73
- LOG_PATH="$(resolve_path "${NTERMINAL_LOG_PATH:-.nterminal/nterminal.log}")"
74
- STATE_PATH="$(resolve_path "${NTERMINAL_STATE_PATH:-.nterminal/state.json}")"
77
+ PID_PATH="$(resolve_path "${NTERMINAL_PID_PATH:-${NTERMINAL_RUNTIME_PID_PATH:-$DEFAULT_PID_PATH}}")"
78
+ LOG_PATH="$(resolve_path "${NTERMINAL_LOG_PATH:-${NTERMINAL_RUNTIME_LOG_PATH:-$DEFAULT_LOG_PATH}}")"
79
+ STATE_PATH="$(resolve_path "${NTERMINAL_STATE_PATH:-${NTERMINAL_RUNTIME_STATE_PATH:-$DEFAULT_STATE_PATH}}")"
75
80
  HEALTH_TIMEOUT_SECONDS="${NTERMINAL_HEALTH_TIMEOUT_SECONDS:-15}"
76
81
  STOP_TIMEOUT_SECONDS="${NTERMINAL_STOP_TIMEOUT_SECONDS:-20}"
77
82
  RUNTIME_ENTRY="$APP_DIR/dist/server/index.js"
@@ -91,6 +96,12 @@ if [[ -n "$UPDATE_LOCK_PATH" ]]; then
91
96
  trap cleanup_update_lock EXIT
92
97
  fi
93
98
 
99
+ export NTERMINAL_APP_DIR="$APP_DIR"
100
+ export NTERMINAL_ENV_FILE="$ENV_FILE"
101
+ export NTERMINAL_STATE_PATH="$STATE_PATH"
102
+ export NTERMINAL_PID_PATH="$PID_PATH"
103
+ export NTERMINAL_LOG_PATH="$LOG_PATH"
104
+
94
105
  health_url() {
95
106
  printf 'http://%s:%s/api/auth/session' "$HOST" "$PORT"
96
107
  }
@@ -103,16 +114,8 @@ ensure_app_dir() {
103
114
  [[ -d "$APP_DIR" ]] || die "app directory not found: $APP_DIR"
104
115
  }
105
116
 
106
- is_source_checkout() {
107
- [[ -f "$APP_DIR/tsconfig.server.json" && -d "$APP_DIR/src" ]]
108
- }
109
-
110
117
  missing_env_message() {
111
- if is_source_checkout; then
112
- printf '.env not found at %s; copy .env.example and run npm run generate:secrets' "$ENV_FILE"
113
- else
114
- printf '.env not found at %s; run nterminal onboarding' "$ENV_FILE"
115
- fi
118
+ printf '.env not found at %s; run nterminal onboarding' "$ENV_FILE"
116
119
  }
117
120
 
118
121
  ensure_env_file() {
@@ -123,9 +126,6 @@ ensure_runtime() {
123
126
  if [[ -f "$RUNTIME_ENTRY" ]]; then
124
127
  return 0
125
128
  fi
126
- if is_source_checkout; then
127
- die "dist/server/index.js not found; run scripts/nterminalctl build first"
128
- fi
129
129
  die "dist/server/index.js not found; reinstall nterminal"
130
130
  }
131
131
 
@@ -198,6 +198,31 @@ launchd_controls_app() {
198
198
  esac
199
199
  }
200
200
 
201
+ launchd_agent_plist() {
202
+ local home_dir="${HOME:-}"
203
+ [[ -n "$home_dir" ]] || return 1
204
+ printf '%s/Library/LaunchAgents/%s.plist' "$home_dir" "$LAUNCHD_LABEL"
205
+ }
206
+
207
+ unload_launchd_if_controlled() {
208
+ launchd_controls_app || return 0
209
+ local domain
210
+ domain="$(launchd_domain)" || return 0
211
+ launchctl bootout "$domain" >/dev/null 2>&1 || launchctl remove "$LAUNCHD_LABEL" >/dev/null 2>&1 || true
212
+ rm -f "$PID_PATH"
213
+ info "NTerminal launchd agent unloaded label=$LAUNCHD_LABEL"
214
+ }
215
+
216
+ remove_owned_launchd_plist() {
217
+ local plist
218
+ plist="$(launchd_agent_plist || true)"
219
+ [[ -n "$plist" && -f "$plist" ]] || return 0
220
+ if grep -F "$APP_DIR" "$plist" >/dev/null 2>&1; then
221
+ rm -f "$plist"
222
+ info "NTerminal launchd plist removed path=$plist"
223
+ fi
224
+ }
225
+
201
226
  remove_stale_pid() {
202
227
  local pid
203
228
  pid="$(read_pid || true)"
@@ -371,37 +396,14 @@ cmd_restart() {
371
396
 
372
397
  cmd_build() {
373
398
  ensure_app_dir
374
- if ! is_source_checkout; then
375
- ensure_runtime
376
- info "Package install uses bundled dist; no build needed."
377
- return 0
378
- fi
379
- (
380
- cd "$APP_DIR"
381
- "$NPM_BIN" run build
382
- )
399
+ ensure_runtime
400
+ info "Package install uses bundled dist; no build needed."
383
401
  }
384
402
 
385
403
  cmd_deploy() {
386
404
  ensure_app_dir
387
405
  ensure_env_file
388
- (
389
- cd "$APP_DIR"
390
- if is_source_checkout && [[ -f package-lock.json ]]; then
391
- "$NPM_BIN" ci
392
- elif is_source_checkout; then
393
- "$NPM_BIN" install
394
- fi
395
- if is_source_checkout; then
396
- "$NPM_BIN" run typecheck
397
- # node-pty's install script always compiles from source (no prebuilt
398
- # download); ask npm to rebuild it explicitly after dependency updates.
399
- "$NPM_BIN" rebuild node-pty --build-from-source
400
- "$NPM_BIN" run build
401
- else
402
- ensure_runtime
403
- fi
404
- )
406
+ ensure_runtime
405
407
  cmd_restart
406
408
  }
407
409
 
@@ -453,15 +455,10 @@ clean_state_paths() {
453
455
  local state_path="$1"
454
456
  local pid_path="$2"
455
457
  local log_path="$3"
456
- local default_state_dir="$4"
457
458
  local state_dir
458
459
  state_dir="$(dirname "$state_path")"
459
- if [[ "$state_dir" == "$default_state_dir" ]]; then
460
- rm -rf "$state_dir"
461
- else
462
- rm -f "$state_path" "$pid_path" "$log_path" "$state_dir/update.lock"
463
- rm -rf "$state_dir/notification-assets"
464
- fi
460
+ rm -f "$state_path" "$pid_path" "$log_path" "$state_dir/update.lock"
461
+ rm -rf "$state_dir/notification-assets"
465
462
  }
466
463
 
467
464
  cmd_clean() {
@@ -479,7 +476,7 @@ cmd_clean() {
479
476
  done
480
477
 
481
478
  if (( force != 1 )); then
482
- die "clean is destructive; rerun with: scripts/nterminalctl clean --force [--env] [--keep-tmux]"
479
+ die "clean is destructive; rerun with: $(control_command clean) --force [--env] [--keep-tmux]"
483
480
  fi
484
481
 
485
482
  ensure_app_dir
@@ -490,7 +487,7 @@ cmd_clean() {
490
487
 
491
488
  local state_dir
492
489
  state_dir="$(dirname "$STATE_PATH")"
493
- clean_state_paths "$STATE_PATH" "$PID_PATH" "$LOG_PATH" "$APP_DIR/.nterminal"
490
+ clean_state_paths "$STATE_PATH" "$PID_PATH" "$LOG_PATH"
494
491
  if (( remove_env == 1 )); then
495
492
  rm -f "$ENV_FILE"
496
493
  fi
@@ -498,6 +495,41 @@ cmd_clean() {
498
495
  info "NTerminal cleaned state_dir=$state_dir env_removed=$remove_env tmux_killed=$kill_tmux"
499
496
  }
500
497
 
498
+ cmd_uninstall() {
499
+ local force=0
500
+ local kill_tmux=1
501
+ while [[ $# -gt 0 ]]; do
502
+ case "$1" in
503
+ --force) force=1 ;;
504
+ --keep-tmux) kill_tmux=0 ;;
505
+ *) die "unknown uninstall option: $1" ;;
506
+ esac
507
+ shift
508
+ done
509
+
510
+ if (( force != 1 )); then
511
+ die "uninstall is destructive; rerun with: $(control_command uninstall) --force [--keep-tmux]"
512
+ fi
513
+
514
+ ensure_app_dir
515
+ unload_launchd_if_controlled
516
+ stop_pid_file "$PID_PATH"
517
+ if (( kill_tmux == 1 )) && command -v tmux >/dev/null 2>&1; then
518
+ tmux -L nterminal kill-server >/dev/null 2>&1 || true
519
+ fi
520
+ clean_state_paths "$STATE_PATH" "$PID_PATH" "$LOG_PATH"
521
+ rm -f "$ENV_FILE"
522
+ remove_owned_launchd_plist
523
+
524
+ info "NTerminal runtime uninstalled state_dir=$(dirname "$STATE_PATH") env_removed=1 tmux_killed=$kill_tmux"
525
+ info "Package files are still installed. To remove them, run: npm uninstall -g nterminal"
526
+ }
527
+
528
+ control_command() {
529
+ local command="$1"
530
+ printf 'nterminal %s' "$command"
531
+ }
532
+
501
533
  cmd_pid() {
502
534
  local pid
503
535
  pid="$(current_pid || true)"
@@ -568,17 +600,20 @@ cmd_doctor() {
568
600
 
569
601
  cmd_help() {
570
602
  cat <<EOF
571
- Usage: scripts/nterminalctl <command>
603
+ Usage: nterminal <command>
572
604
 
573
605
  Commands:
574
606
  status Show process status. Exit 0 when running, 3 when stopped.
575
607
  start Start the built server in the background.
576
608
  stop Stop the managed server with SIGTERM, then SIGKILL after timeout.
577
609
  restart Stop and start the server.
578
- build Build source checkouts. Package installs use bundled dist.
579
- deploy Build source checkouts when needed, then restart.
610
+ build No-op for package installs; verifies the bundled runtime exists.
611
+ deploy Restart the installed package using the bundled runtime.
580
612
  clean --force Stop server, kill NTerminal tmux sessions, and remove local state/log/pid files.
581
613
  Add --env to also remove .env. Add --keep-tmux to preserve live panes.
614
+ uninstall --force
615
+ Stop server, unload NTerminal launchd agent, remove local runtime files and .env.
616
+ Add --keep-tmux to preserve live panes.
582
617
  logs [N] Show the last N log lines. Default: 100.
583
618
  logs -f [N] Follow logs.
584
619
  doctor Check Node/npm/env/runtime/pid/log/port readiness.
@@ -589,9 +624,9 @@ Commands:
589
624
  Environment:
590
625
  NTERMINAL_APP_DIR Override app directory.
591
626
  NTERMINAL_ENV_FILE Override .env path.
592
- NTERMINAL_STATE_PATH Default .nterminal/state.json.
593
- NTERMINAL_PID_PATH Default .nterminal/nterminal.pid.
594
- NTERMINAL_LOG_PATH Default .nterminal/nterminal.log.
627
+ NTERMINAL_STATE_PATH Default ~/.nterminal/state.json.
628
+ NTERMINAL_PID_PATH Default ~/.nterminal/nterminal.pid.
629
+ NTERMINAL_LOG_PATH Default ~/.nterminal/nterminal.log.
595
630
  NTERMINAL_HEALTH_TIMEOUT_SECONDS Default 15.
596
631
  NTERMINAL_STOP_TIMEOUT_SECONDS Default 20.
597
632
  NTERMINAL_LAUNCHD_LABEL Default com.nterminal.local. Used when a loaded LaunchAgent controls this app.
@@ -606,6 +641,7 @@ case "$COMMAND" in
606
641
  build) cmd_build "$@" ;;
607
642
  deploy) cmd_deploy "$@" ;;
608
643
  clean|purge) cmd_clean "$@" ;;
644
+ uninstall|remove) cmd_uninstall "$@" ;;
609
645
  logs|log) cmd_logs "$@" ;;
610
646
  doctor) cmd_doctor "$@" ;;
611
647
  pid) cmd_pid "$@" ;;