nterminal 1.2.22 → 1.2.24
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.
- package/bin/nterminal.js +1 -0
- package/dist/client/assets/{MarkdownPreview-D8zAIfDx.js → MarkdownPreview-DCfGMtwX.js} +1 -1
- package/dist/client/assets/{index-Bzw1v9JR.css → index-f-1XNznv.css} +1 -1
- package/dist/client/assets/{index-CXBkB4aI.js → index-ljxLHcWv.js} +22 -26
- package/dist/client/index.html +2 -2
- package/dist/scripts/onboarding.js +241 -3
- package/dist/scripts/onboarding.js.map +1 -1
- package/dist/server/agent/agentRoutes.js +11 -0
- package/dist/server/agent/agentRoutes.js.map +1 -1
- package/dist/server/config.d.ts +1 -0
- package/dist/server/config.js +12 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/index.js +44 -35
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/updateRoutes.js +11 -1
- package/dist/server/routes/updateRoutes.js.map +1 -1
- package/dist/server/runtimeState.d.ts +4 -0
- package/dist/server/runtimeState.js +13 -0
- package/dist/server/runtimeState.js.map +1 -0
- package/dist/server/storage/fileStore.d.ts +11 -1
- package/dist/server/storage/fileStore.js +20 -1
- package/dist/server/storage/fileStore.js.map +1 -1
- package/dist/server/terminal/transcriptHistory.js +6 -6
- package/dist/server/terminal/transcriptHistory.js.map +1 -1
- package/dist/server/update/packageUpdate.js +50 -10
- package/dist/server/update/packageUpdate.js.map +1 -1
- package/dist/server/update/stateCheckpoint.d.ts +3 -0
- package/dist/server/update/stateCheckpoint.js +34 -0
- package/dist/server/update/stateCheckpoint.js.map +1 -0
- package/dist/shared/manualUpdate.d.ts +1 -1
- package/dist/shared/manualUpdate.js +2 -2
- package/dist/shared/manualUpdate.js.map +1 -1
- package/docs/configuration.md +1 -0
- package/docs/features.md +2 -2
- package/docs/onboarding.md +1 -1
- package/docs/operations.md +4 -2
- package/package.json +1 -1
- package/scripts/nterminalctl +91 -0
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { createManualUpdateInfo, isPermissionUpdateFailure } from '../../shared/manualUpdate.js';
|
|
6
6
|
const updateLockStaleMs = 10 * 60 * 1000;
|
|
7
7
|
const packageUpdateTimeoutMs = 5 * 60 * 1000;
|
|
8
|
+
const defaultUpdateHelperPath = '/usr/local/sbin/nterminal-update';
|
|
8
9
|
function appDir() {
|
|
9
10
|
return process.env.NTERMINAL_APP_DIR ? path.resolve(process.env.NTERMINAL_APP_DIR) : process.cwd();
|
|
10
11
|
}
|
|
@@ -16,6 +17,27 @@ function npm(args, cwd, timeout = 60_000) {
|
|
|
16
17
|
stderr: (result.stderr ?? '').trim()
|
|
17
18
|
};
|
|
18
19
|
}
|
|
20
|
+
function updateHelperPath() {
|
|
21
|
+
return process.env.NTERMINAL_UPDATE_HELPER_PATH?.trim() || defaultUpdateHelperPath;
|
|
22
|
+
}
|
|
23
|
+
function sudoBin() {
|
|
24
|
+
return process.env.NTERMINAL_SUDO_BIN?.trim() || 'sudo';
|
|
25
|
+
}
|
|
26
|
+
function runUpdateHelper(action, cwd, timeout = 60_000) {
|
|
27
|
+
const result = spawnSync(sudoBin(), ['-n', updateHelperPath(), action], { cwd, encoding: 'utf8', timeout });
|
|
28
|
+
return {
|
|
29
|
+
ok: result.status === 0,
|
|
30
|
+
stdout: (result.stdout ?? '').trim(),
|
|
31
|
+
stderr: (result.stderr ?? '').trim()
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function helperOutput(result) {
|
|
35
|
+
return result.stderr || result.stdout || 'privileged update helper failed';
|
|
36
|
+
}
|
|
37
|
+
function isHelperPermissionFailure(output) {
|
|
38
|
+
return (isPermissionUpdateFailure(output) ||
|
|
39
|
+
/sudo|password is required|a password is required|not in the sudoers file|not allowed|authentication|privileged update helper/i.test(output));
|
|
40
|
+
}
|
|
19
41
|
function readPackageName(cwd) {
|
|
20
42
|
try {
|
|
21
43
|
const pkg = JSON.parse(readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
@@ -136,19 +158,37 @@ export function runUpdate(cwd = appDir()) {
|
|
|
136
158
|
if (!check.latestVersion || installedVersion !== check.latestVersion) {
|
|
137
159
|
const install = npm(['install', '-g', `${packageName}@latest`, '--prefer-online'], cwd, packageUpdateTimeoutMs);
|
|
138
160
|
if (!install.ok) {
|
|
139
|
-
releaseUpdateLock(cwd);
|
|
140
161
|
const output = install.stderr || install.stdout || 'npm install -g failed';
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
const helperCheck = isPermissionUpdateFailure(output) ? runUpdateHelper('check', cwd) : null;
|
|
163
|
+
if (helperCheck?.ok) {
|
|
164
|
+
const helperInstall = runUpdateHelper('install', cwd, packageUpdateTimeoutMs);
|
|
165
|
+
if (!helperInstall.ok) {
|
|
166
|
+
releaseUpdateLock(cwd);
|
|
167
|
+
const elevatedOutput = helperOutput(helperInstall);
|
|
168
|
+
const manualUpdate = isHelperPermissionFailure(elevatedOutput) ? createManualUpdateInfo(packageName) : null;
|
|
169
|
+
return {
|
|
170
|
+
ok: false,
|
|
171
|
+
changed: false,
|
|
172
|
+
message: manualUpdate?.message ?? elevatedOutput,
|
|
173
|
+
restarting: false,
|
|
174
|
+
...(manualUpdate ? { manualUpdate } : {})
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
releaseUpdateLock(cwd);
|
|
180
|
+
const manualUpdate = isPermissionUpdateFailure(output) || (helperCheck && isHelperPermissionFailure(helperOutput(helperCheck))) ? createManualUpdateInfo(packageName) : null;
|
|
181
|
+
return {
|
|
182
|
+
ok: false,
|
|
183
|
+
changed: false,
|
|
184
|
+
message: manualUpdate?.message ?? output,
|
|
185
|
+
restarting: false,
|
|
186
|
+
...(manualUpdate ? { manualUpdate } : {})
|
|
187
|
+
};
|
|
188
|
+
}
|
|
149
189
|
}
|
|
150
190
|
}
|
|
151
|
-
const restart = spawn('sh', ['-c', 'sleep 0.2; exec bash "$1" restart', 'nterminal-restart', path.join(cwd, 'scripts/nterminalctl')], {
|
|
191
|
+
const restart = spawn('sh', ['-c', 'sleep 0.2; exec bash "$1" restart-after-update', 'nterminal-restart', path.join(cwd, 'scripts/nterminalctl')], {
|
|
152
192
|
cwd,
|
|
153
193
|
detached: true,
|
|
154
194
|
env: { ...process.env, NTERMINAL_UPDATE_LOCK_PATH: lockPath(cwd) },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageUpdate.js","sourceRoot":"","sources":["../../../src/server/update/packageUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1G,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAEjG,MAAM,iBAAiB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACzC,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"packageUpdate.js","sourceRoot":"","sources":["../../../src/server/update/packageUpdate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export async function checkpointStateBeforeUpdate(config, fileStore) {
|
|
2
|
+
const checkpointOptions = config.role === 'main' ? { validateSnapshot: validateMainSnapshot } : {};
|
|
3
|
+
const { snapshot, disk } = await fileStore.checkpoint(checkpointOptions);
|
|
4
|
+
if (config.role === 'main') {
|
|
5
|
+
validateMainDiskCheckpoint(snapshot, disk);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function validateMainSnapshot(snapshot) {
|
|
9
|
+
requireMainPassword(snapshot);
|
|
10
|
+
}
|
|
11
|
+
function validateMainDiskCheckpoint(snapshot, disk) {
|
|
12
|
+
const snapshotPassword = requireMainPassword(snapshot);
|
|
13
|
+
if (!disk.passwordCredential) {
|
|
14
|
+
throw new Error('main state checkpoint did not preserve the password credential on disk');
|
|
15
|
+
}
|
|
16
|
+
if (disk.passwordCredential.hash !== snapshotPassword.hash ||
|
|
17
|
+
disk.passwordCredential.salt !== snapshotPassword.salt ||
|
|
18
|
+
disk.passwordCredential.createdAt !== snapshotPassword.createdAt ||
|
|
19
|
+
disk.passwordCredential.updatedAt !== snapshotPassword.updatedAt) {
|
|
20
|
+
throw new Error('main state checkpoint changed the password credential on disk');
|
|
21
|
+
}
|
|
22
|
+
const diskAgentIds = new Set(disk.agents.map((agent) => agent.id));
|
|
23
|
+
const missingAgentIds = snapshot.agents.map((agent) => agent.id).filter((agentId) => !diskAgentIds.has(agentId));
|
|
24
|
+
if (missingAgentIds.length > 0) {
|
|
25
|
+
throw new Error(`main state checkpoint lost ${missingAgentIds.length} registered secondary server(s)`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function requireMainPassword(snapshot) {
|
|
29
|
+
if (!snapshot.passwordCredential) {
|
|
30
|
+
throw new Error('main state has no password credential in memory');
|
|
31
|
+
}
|
|
32
|
+
return snapshot.passwordCredential;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=stateCheckpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateCheckpoint.js","sourceRoot":"","sources":["../../../src/server/update/stateCheckpoint.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,MAAiB,EAAE,SAAoB;IACvF,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnG,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,0BAA0B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAqB;IACjD,mBAAmB,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,0BAA0B,CAAC,QAAqB,EAAE,IAAiB;IAC1E,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC5F,CAAC;IACD,IACE,IAAI,CAAC,kBAAkB,CAAC,IAAI,KAAK,gBAAgB,CAAC,IAAI;QACtD,IAAI,CAAC,kBAAkB,CAAC,IAAI,KAAK,gBAAgB,CAAC,IAAI;QACtD,IAAI,CAAC,kBAAkB,CAAC,SAAS,KAAK,gBAAgB,CAAC,SAAS;QAChE,IAAI,CAAC,kBAAkB,CAAC,SAAS,KAAK,gBAAgB,CAAC,SAAS,EAChE,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACnE,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IACjH,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,8BAA8B,eAAe,CAAC,MAAM,iCAAiC,CAAC,CAAC;IACzG,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAqB;IAChD,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,QAAQ,CAAC,kBAAkB,CAAC;AACrC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ManualUpdateInfo } from './protocol.js';
|
|
2
|
-
export declare const manualUpdateMessage = "Automatic update needs write access to the global npm install. Run
|
|
2
|
+
export declare const manualUpdateMessage = "Automatic update needs write access to the global npm install. Run this from a normal NTerminal runtime-user shell; only the npm install step uses sudo.";
|
|
3
3
|
export declare function createManualUpdateInfo(packageName?: string, detectedAt?: Date): ManualUpdateInfo;
|
|
4
4
|
export declare function isPermissionUpdateFailure(output: string): boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export const manualUpdateMessage = 'Automatic update needs write access to the global npm install. Run
|
|
1
|
+
export const manualUpdateMessage = 'Automatic update needs write access to the global npm install. Run this from a normal NTerminal runtime-user shell; only the npm install step uses sudo.';
|
|
2
2
|
export function createManualUpdateInfo(packageName = 'nterminal', detectedAt = new Date()) {
|
|
3
3
|
return {
|
|
4
4
|
reason: 'permission',
|
|
5
5
|
message: manualUpdateMessage,
|
|
6
|
-
command: `sudo npm i -g ${packageName}@latest --prefer-online && nterminal restart`,
|
|
6
|
+
command: `sudo npm i -g ${packageName}@latest --prefer-online && nterminal restart-after-update`,
|
|
7
7
|
detectedAt: detectedAt.toISOString()
|
|
8
8
|
};
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manualUpdate.js","sourceRoot":"","sources":["../../src/shared/manualUpdate.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,mBAAmB,GAC9B,
|
|
1
|
+
{"version":3,"file":"manualUpdate.js","sourceRoot":"","sources":["../../src/shared/manualUpdate.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,mBAAmB,GAC9B,0JAA0J,CAAC;AAE7J,MAAM,UAAU,sBAAsB,CAAC,WAAW,GAAG,WAAW,EAAE,UAAU,GAAG,IAAI,IAAI,EAAE;IACvF,OAAO;QACL,MAAM,EAAE,YAAY;QACpB,OAAO,EAAE,mBAAmB;QAC5B,OAAO,EAAE,iBAAiB,WAAW,2DAA2D;QAChG,UAAU,EAAE,UAAU,CAAC,WAAW,EAAE;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAc;IACtD,OAAO,qKAAqK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5L,CAAC"}
|
package/docs/configuration.md
CHANGED
|
@@ -20,6 +20,7 @@ Onboarding writes `.env` for you, including an absolute `NTERMINAL_STATE_PATH` s
|
|
|
20
20
|
|
|
21
21
|
| Setting | Default | Purpose |
|
|
22
22
|
| --- | --- | --- |
|
|
23
|
+
| `NTERMINAL_ROLE` | required | `main` or `secondary`. Onboarding writes it; runtime startup fails if it is missing. |
|
|
23
24
|
| `NTERMINAL_HOST` | `127.0.0.1` | Bind host. Keep the main server local unless a separate access boundary is in front. |
|
|
24
25
|
| `NTERMINAL_PORT` | `3107` | HTTP server port. |
|
|
25
26
|
| `NTERMINAL_WORKSPACE_ROOT` | home directory | Startup directory for new terminal tabs. |
|
package/docs/features.md
CHANGED
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
- Mouse wheel scrolling is captured for terminal output history instead of Codex/Claude prompt history.
|
|
45
45
|
- tmux-backed panes load older scrollback incrementally when the viewport reaches the top.
|
|
46
46
|
- Scroll progress indicators, jump-to-bottom controls, and follow-tail behavior keep large outputs usable.
|
|
47
|
-
- Supported Codex/Claude panes show a `
|
|
47
|
+
- Supported Codex/Claude panes show a `View history` header button and can continue into archived transcript history.
|
|
48
48
|
- JSONL transcript history is loaded by cursor only when NTerminal can determine the session source unambiguously.
|
|
49
|
-
- The
|
|
49
|
+
- The history viewer opens near fullscreen, renders the transcript as terminal-like raw output, starts at the newest line, and loads older entries as you scroll upward.
|
|
50
50
|
|
|
51
51
|
## Files
|
|
52
52
|
|
package/docs/onboarding.md
CHANGED
|
@@ -78,6 +78,6 @@ The "Main is ready" summary is printed only after the selected setup path has co
|
|
|
78
78
|
|
|
79
79
|
You can re-run onboarding to refresh generated config or register a secondary server again.
|
|
80
80
|
|
|
81
|
-
Existing `
|
|
81
|
+
Existing `NTERMINAL_PORT`, `NTERMINAL_SESSION_SECRET`, and `NTERMINAL_AGENT_TOKEN` values are preserved on re-run. Main onboarding always forces `NTERMINAL_HOST=127.0.0.1` so public traffic goes through nginx/TLS; secondary onboarding preserves its existing bind host.
|
|
82
82
|
|
|
83
83
|
Registering the same secondary server URL again replaces the previous entry for that URL.
|
package/docs/operations.md
CHANGED
|
@@ -34,14 +34,16 @@ Each server updates itself as an npm package install.
|
|
|
34
34
|
The **updates** panel in the main server sidebar footer can manage the fleet:
|
|
35
35
|
|
|
36
36
|
- **Check for updates** checks the npm registry.
|
|
37
|
-
- **Update** runs `npm install -g nterminal@latest --prefer-online` plus restart.
|
|
38
|
-
- **Update all** updates secondary servers in parallel first
|
|
37
|
+
- **Update** runs `npm install -g nterminal@latest --prefer-online` plus a package-aware restart.
|
|
38
|
+
- **Update all** updates secondary servers in parallel first, then still attempts the main server last even if a secondary failed.
|
|
39
39
|
|
|
40
40
|
Notes:
|
|
41
41
|
|
|
42
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.
|
|
43
|
+
- If the global npm prefix is root-owned, onboarding can install a restricted sudo helper pinned to the detected npm prefix for `nterminal@latest` updates only.
|
|
43
44
|
- An npm registry or permission error is reported as an explanatory status.
|
|
44
45
|
- Update endpoints require the same auth boundary as the rest of NTerminal.
|
|
46
|
+
- Runtime startup refuses to create missing main state. If `~/.nterminal/state.json` is missing or has no password credential, restore it or run onboarding instead of booting into a new empty setup.
|
|
45
47
|
|
|
46
48
|
## Clean Uninstall
|
|
47
49
|
|
package/package.json
CHANGED
package/scripts/nterminalctl
CHANGED
|
@@ -81,6 +81,7 @@ LOG_PATH="$(resolve_path "${NTERMINAL_LOG_PATH:-${NTERMINAL_RUNTIME_LOG_PATH:-$D
|
|
|
81
81
|
STATE_PATH="$(resolve_path "${NTERMINAL_STATE_PATH:-${NTERMINAL_RUNTIME_STATE_PATH:-$DEFAULT_STATE_PATH}}")"
|
|
82
82
|
HEALTH_TIMEOUT_SECONDS="${NTERMINAL_HEALTH_TIMEOUT_SECONDS:-15}"
|
|
83
83
|
STOP_TIMEOUT_SECONDS="${NTERMINAL_STOP_TIMEOUT_SECONDS:-20}"
|
|
84
|
+
FATAL_STARTUP_EXIT_CODE="${NTERMINAL_FATAL_STARTUP_EXIT_CODE:-78}"
|
|
84
85
|
RUNTIME_ENTRY="$APP_DIR/dist/server/index.js"
|
|
85
86
|
LAUNCHD_LABEL="${NTERMINAL_LAUNCHD_LABEL:-com.nterminal.local}"
|
|
86
87
|
UPDATE_LOCK_PATH="${NTERMINAL_UPDATE_LOCK_PATH:-}"
|
|
@@ -147,9 +148,41 @@ validate_required_env() {
|
|
|
147
148
|
missing=1
|
|
148
149
|
fi
|
|
149
150
|
done
|
|
151
|
+
if [[ "${NTERMINAL_ROLE:-}" != "main" && "${NTERMINAL_ROLE:-}" != "secondary" ]]; then
|
|
152
|
+
echo "missing or invalid env: NTERMINAL_ROLE (expected main or secondary)"
|
|
153
|
+
missing=1
|
|
154
|
+
fi
|
|
155
|
+
if [[ "${NTERMINAL_ROLE:-}" == "secondary" ]]; then
|
|
156
|
+
local agent_token="${NTERMINAL_AGENT_TOKEN:-}"
|
|
157
|
+
if (( ${#agent_token} < 32 )); then
|
|
158
|
+
echo "missing or too-short env: NTERMINAL_AGENT_TOKEN"
|
|
159
|
+
missing=1
|
|
160
|
+
fi
|
|
161
|
+
fi
|
|
150
162
|
return "$missing"
|
|
151
163
|
}
|
|
152
164
|
|
|
165
|
+
doctor_runtime_state() {
|
|
166
|
+
local role="${NTERMINAL_ROLE:-}"
|
|
167
|
+
if [[ "$role" != "main" && "$role" != "secondary" ]]; then
|
|
168
|
+
return 1
|
|
169
|
+
fi
|
|
170
|
+
if [[ ! -f "$STATE_PATH" ]]; then
|
|
171
|
+
echo "fail state: state file not found at $STATE_PATH"
|
|
172
|
+
return 1
|
|
173
|
+
fi
|
|
174
|
+
if [[ "$role" == "secondary" ]]; then
|
|
175
|
+
echo "ok state: $STATE_PATH"
|
|
176
|
+
return 0
|
|
177
|
+
fi
|
|
178
|
+
if "$NODE_BIN" -e 'const fs = require("node:fs"); const state = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); process.exit(state && typeof state.passwordCredential === "object" && state.passwordCredential ? 0 : 2);' "$STATE_PATH" >/dev/null 2>&1; then
|
|
179
|
+
echo "ok state: main password credential present"
|
|
180
|
+
return 0
|
|
181
|
+
fi
|
|
182
|
+
echo "fail state: main state has no passwordCredential at $STATE_PATH"
|
|
183
|
+
return 1
|
|
184
|
+
}
|
|
185
|
+
|
|
153
186
|
prepare_start() {
|
|
154
187
|
ensure_app_dir
|
|
155
188
|
ensure_env_file
|
|
@@ -438,6 +471,11 @@ cmd_supervise() {
|
|
|
438
471
|
rm -f "$MONITOR_PID_PATH"
|
|
439
472
|
exit 0
|
|
440
473
|
fi
|
|
474
|
+
if (( status == FATAL_STARTUP_EXIT_CODE )); then
|
|
475
|
+
printf '[%s] NTerminal process exited with fatal startup status=%s; supervisor will not restart it\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$status" >> "$LOG_PATH"
|
|
476
|
+
rm -f "$MONITOR_PID_PATH"
|
|
477
|
+
exit "$status"
|
|
478
|
+
fi
|
|
441
479
|
|
|
442
480
|
printf '[%s] NTerminal process exited status=%s; restarting in 2s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$status" >> "$LOG_PATH"
|
|
443
481
|
sleep 2
|
|
@@ -480,6 +518,30 @@ wait_for_health() {
|
|
|
480
518
|
return 1
|
|
481
519
|
}
|
|
482
520
|
|
|
521
|
+
restart_supervised_child() {
|
|
522
|
+
local monitor_pid="$1"
|
|
523
|
+
local old_pid="${2:-}"
|
|
524
|
+
|
|
525
|
+
if [[ -n "$old_pid" ]]; then
|
|
526
|
+
stop_supervised_child "$old_pid"
|
|
527
|
+
fi
|
|
528
|
+
|
|
529
|
+
local pid=""
|
|
530
|
+
local deadline=$((SECONDS + HEALTH_TIMEOUT_SECONDS))
|
|
531
|
+
while (( SECONDS <= deadline )); do
|
|
532
|
+
if ! is_running "$monitor_pid"; then
|
|
533
|
+
return 1
|
|
534
|
+
fi
|
|
535
|
+
pid="$(current_pid || true)"
|
|
536
|
+
if [[ -n "$pid" && "$pid" != "$old_pid" ]] && wait_for_health "$pid"; then
|
|
537
|
+
info "NTerminal restarted pid=$pid supervisor=$monitor_pid url=$(public_url) log=$LOG_PATH"
|
|
538
|
+
return 0
|
|
539
|
+
fi
|
|
540
|
+
sleep 0.25
|
|
541
|
+
done
|
|
542
|
+
return 1
|
|
543
|
+
}
|
|
544
|
+
|
|
483
545
|
port_owner_pid() {
|
|
484
546
|
if command -v lsof >/dev/null 2>&1; then
|
|
485
547
|
lsof -nP -iTCP:"$PORT" -sTCP:LISTEN -t 2>/dev/null | head -n 1
|
|
@@ -645,12 +707,37 @@ cmd_restart() {
|
|
|
645
707
|
start_launchd
|
|
646
708
|
else
|
|
647
709
|
prepare_start
|
|
710
|
+
remove_stale_pid
|
|
711
|
+
remove_stale_monitor_pid
|
|
712
|
+
local monitor_pid
|
|
713
|
+
monitor_pid="$(current_monitor_pid || true)"
|
|
714
|
+
if [[ -n "$monitor_pid" ]]; then
|
|
715
|
+
local pid
|
|
716
|
+
pid="$(current_pid || true)"
|
|
717
|
+
if restart_supervised_child "$monitor_pid" "$pid"; then
|
|
718
|
+
return 0
|
|
719
|
+
fi
|
|
720
|
+
echo "NTerminal failed to restart under supervisor. Last log lines:" >&2
|
|
721
|
+
tail -n 40 "$LOG_PATH" >&2 || true
|
|
722
|
+
return 1
|
|
723
|
+
fi
|
|
648
724
|
cmd_stop >/dev/null
|
|
649
725
|
cmd_start
|
|
650
726
|
fi
|
|
651
727
|
info "NTerminal restarted"
|
|
652
728
|
}
|
|
653
729
|
|
|
730
|
+
cmd_restart_after_update() {
|
|
731
|
+
prepare_start
|
|
732
|
+
if launchd_controls_app; then
|
|
733
|
+
start_launchd
|
|
734
|
+
info "NTerminal restarted after update"
|
|
735
|
+
return 0
|
|
736
|
+
fi
|
|
737
|
+
cmd_stop >/dev/null || true
|
|
738
|
+
cmd_start
|
|
739
|
+
}
|
|
740
|
+
|
|
654
741
|
cmd_build() {
|
|
655
742
|
ensure_app_dir
|
|
656
743
|
ensure_runtime
|
|
@@ -804,6 +891,7 @@ cmd_doctor() {
|
|
|
804
891
|
echo "fail env: .env not found at $ENV_FILE"
|
|
805
892
|
failed=1
|
|
806
893
|
fi
|
|
894
|
+
doctor_runtime_state || failed=1
|
|
807
895
|
|
|
808
896
|
if [[ -f "$RUNTIME_ENTRY" ]]; then
|
|
809
897
|
echo "ok runtime: dist/server/index.js"
|
|
@@ -842,6 +930,8 @@ Commands:
|
|
|
842
930
|
start Start the built server in the background.
|
|
843
931
|
stop Stop the managed server and supervisor with SIGTERM, then SIGKILL after timeout.
|
|
844
932
|
restart Stop and start the server.
|
|
933
|
+
restart-after-update
|
|
934
|
+
Stop the supervisor and start again after an npm package update.
|
|
845
935
|
build No-op for package installs; verifies the bundled runtime exists.
|
|
846
936
|
deploy Restart the installed package using the bundled runtime.
|
|
847
937
|
clean --force Stop server, kill NTerminal tmux sessions, and remove local state/log/pid files.
|
|
@@ -875,6 +965,7 @@ case "$COMMAND" in
|
|
|
875
965
|
supervise) cmd_supervise "$@" ;;
|
|
876
966
|
stop) cmd_stop "$@" ;;
|
|
877
967
|
restart|reload) cmd_restart "$@" ;;
|
|
968
|
+
restart-after-update) cmd_restart_after_update "$@" ;;
|
|
878
969
|
build) cmd_build "$@" ;;
|
|
879
970
|
deploy) cmd_deploy "$@" ;;
|
|
880
971
|
clean|purge) cmd_clean "$@" ;;
|