nterminal 1.3.4 → 1.3.6
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/README.md +68 -37
- package/assets/screenshot/accounts.png +0 -0
- package/assets/screenshot/history.png +0 -0
- package/assets/screenshot/mobile-panes.png +0 -0
- package/assets/screenshot/mobile.png +0 -0
- package/assets/screenshot/scr.png +0 -0
- package/assets/screenshot/subagents.png +0 -0
- package/bin/nterminal.js +10 -3
- package/dist/client/assets/MarkdownPreview-B-LrTjTM.js +1 -0
- package/dist/client/assets/{TranscriptMarkdownBody-H8ZsISc6.js → TranscriptMarkdownBody-3aYVmqQz.js} +1 -1
- package/dist/client/assets/index-D3LN5iAH.js +58 -0
- package/dist/client/assets/{index-DxfBq-4P.js → index-Dd_o8KCL.js} +1 -1
- package/dist/client/assets/index-W-zGfY1a.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/scripts/hooks.js +124 -0
- package/dist/scripts/hooks.js.map +1 -0
- package/dist/server/accounts/accountService.d.ts +23 -1
- package/dist/server/accounts/accountService.js +235 -103
- package/dist/server/accounts/accountService.js.map +1 -1
- package/dist/server/accounts/providerRequests.d.ts +12 -0
- package/dist/server/accounts/providerRequests.js +159 -0
- package/dist/server/accounts/providerRequests.js.map +1 -0
- package/dist/server/agent/agentRoutes.js +11 -0
- package/dist/server/agent/agentRoutes.js.map +1 -1
- package/dist/server/hooks/codexAppServer.d.ts +30 -0
- package/dist/server/hooks/codexAppServer.js +170 -0
- package/dist/server/hooks/codexAppServer.js.map +1 -0
- package/dist/server/hooks/codexTrust.d.ts +72 -0
- package/dist/server/hooks/codexTrust.js +116 -0
- package/dist/server/hooks/codexTrust.js.map +1 -0
- package/dist/server/hooks/helperJournal.d.ts +16 -0
- package/dist/server/hooks/helperJournal.js +74 -0
- package/dist/server/hooks/helperJournal.js.map +1 -0
- package/dist/server/hooks/hookBinding.d.ts +39 -0
- package/dist/server/hooks/hookBinding.js +91 -0
- package/dist/server/hooks/hookBinding.js.map +1 -0
- package/dist/server/hooks/hookFiles.d.ts +24 -0
- package/dist/server/hooks/hookFiles.js +160 -0
- package/dist/server/hooks/hookFiles.js.map +1 -0
- package/dist/server/hooks/hookInstallation.d.ts +108 -0
- package/dist/server/hooks/hookInstallation.js +1074 -0
- package/dist/server/hooks/hookInstallation.js.map +1 -0
- package/dist/server/hooks/hookProcess.d.ts +5 -0
- package/dist/server/hooks/hookProcess.js +85 -0
- package/dist/server/hooks/hookProcess.js.map +1 -0
- package/dist/server/hooks/hookReceiver.d.ts +66 -0
- package/dist/server/hooks/hookReceiver.js +549 -0
- package/dist/server/hooks/hookReceiver.js.map +1 -0
- package/dist/server/hooks/lifecycle.d.ts +40 -0
- package/dist/server/hooks/lifecycle.js +200 -0
- package/dist/server/hooks/lifecycle.js.map +1 -0
- package/dist/server/hooks/publicationLock.d.ts +3 -0
- package/dist/server/hooks/publicationLock.js +137 -0
- package/dist/server/hooks/publicationLock.js.map +1 -0
- package/dist/server/hooks/verifiedLock.d.ts +24 -0
- package/dist/server/hooks/verifiedLock.js +207 -0
- package/dist/server/hooks/verifiedLock.js.map +1 -0
- package/dist/server/http.d.ts +4 -0
- package/dist/server/http.js +6 -0
- package/dist/server/http.js.map +1 -1
- package/dist/server/index.js +71 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/notifications/agentCompletionMonitor.d.ts +16 -1
- package/dist/server/notifications/agentCompletionMonitor.js +261 -47
- package/dist/server/notifications/agentCompletionMonitor.js.map +1 -1
- package/dist/server/notifications/completionNotificationRouter.d.ts +2 -0
- package/dist/server/notifications/completionNotificationRouter.js +9 -0
- package/dist/server/notifications/completionNotificationRouter.js.map +1 -1
- package/dist/server/routes/accountRoutes.js +11 -0
- package/dist/server/routes/accountRoutes.js.map +1 -1
- package/dist/server/routes/activityRoutes.d.ts +13 -0
- package/dist/server/routes/activityRoutes.js +165 -0
- package/dist/server/routes/activityRoutes.js.map +1 -0
- package/dist/server/routes/agentManagementRoutes.js +8 -1
- package/dist/server/routes/agentManagementRoutes.js.map +1 -1
- package/dist/server/routes/hookRoutes.d.ts +16 -0
- package/dist/server/routes/hookRoutes.js +109 -0
- package/dist/server/routes/hookRoutes.js.map +1 -0
- package/dist/server/routes/terminalRoutes.js +4 -0
- package/dist/server/routes/terminalRoutes.js.map +1 -1
- package/dist/server/routes/transcriptQuery.d.ts +2 -1
- package/dist/server/routes/transcriptQuery.js +3 -1
- package/dist/server/routes/transcriptQuery.js.map +1 -1
- package/dist/server/terminal/PtyAdapter.d.ts +19 -0
- package/dist/server/terminal/PtyAdapter.js.map +1 -1
- package/dist/server/terminal/TerminalManager.d.ts +61 -0
- package/dist/server/terminal/TerminalManager.js +672 -12
- package/dist/server/terminal/TerminalManager.js.map +1 -1
- package/dist/server/terminal/TmuxPtyAdapter.d.ts +14 -2
- package/dist/server/terminal/TmuxPtyAdapter.js +101 -15
- package/dist/server/terminal/TmuxPtyAdapter.js.map +1 -1
- package/dist/server/terminal/activityChannel.d.ts +57 -0
- package/dist/server/terminal/activityChannel.js +166 -0
- package/dist/server/terminal/activityChannel.js.map +1 -0
- package/dist/server/terminal/agentTranscriptStatus.d.ts +12 -0
- package/dist/server/terminal/agentTranscriptStatus.js +78 -7
- package/dist/server/terminal/agentTranscriptStatus.js.map +1 -1
- package/dist/server/terminal/codexTranscriptSource.d.ts +3 -0
- package/dist/server/terminal/codexTranscriptSource.js +99 -39
- package/dist/server/terminal/codexTranscriptSource.js.map +1 -1
- package/dist/server/terminal/subagentSources.js +2 -1
- package/dist/server/terminal/subagentSources.js.map +1 -1
- package/dist/server/terminal/transcriptHeader.d.ts +85 -0
- package/dist/server/terminal/transcriptHeader.js +376 -0
- package/dist/server/terminal/transcriptHeader.js.map +1 -0
- package/dist/server/terminal/transcriptHistory.d.ts +16 -2
- package/dist/server/terminal/transcriptHistory.js +120 -21
- package/dist/server/terminal/transcriptHistory.js.map +1 -1
- package/dist/server/terminal/transcriptStore.d.ts +6 -0
- package/dist/server/terminal/transcriptStore.js +222 -26
- package/dist/server/terminal/transcriptStore.js.map +1 -1
- package/dist/shared/activityProbe.d.ts +10 -0
- package/dist/shared/activityProbe.js +24 -0
- package/dist/shared/activityProbe.js.map +1 -0
- package/dist/shared/protocol.d.ts +45 -0
- package/dist/shared/protocol.js.map +1 -1
- package/dist/shared/types.d.ts +9 -1
- package/docs/features.md +21 -1
- package/docs/provider-hooks.md +340 -0
- package/docs/terminal-history.md +10 -0
- package/package.json +2 -1
- package/scripts/nterminal-hook.cjs +171 -0
- package/scripts/nterminalctl +26 -2
- package/dist/client/assets/MarkdownPreview-DjDO4lEl.js +0 -1
- package/dist/client/assets/index-CIYazAGv.css +0 -1
- package/dist/client/assets/index-DwvLN_WJ.js +0 -56
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Transport controls are deliberately not activity journal or monitor events.
|
|
2
|
+
export const activityProbeCapability = 'nonce-v1';
|
|
3
|
+
export const activityProbeMaxBytes = 512;
|
|
4
|
+
export const activityProbeIntervalMs = 30_000;
|
|
5
|
+
export const activityProbeDeadlineMs = 10_000;
|
|
6
|
+
export function decodeActivityProbe(text) {
|
|
7
|
+
if (text.length > activityProbeMaxBytes || new TextEncoder().encode(text).byteLength > activityProbeMaxBytes)
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
const value = JSON.parse(text);
|
|
11
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
12
|
+
return null;
|
|
13
|
+
const probe = value;
|
|
14
|
+
if (Object.keys(probe).length !== 3 || !Object.hasOwn(probe, 'type') || !Object.hasOwn(probe, 'version')
|
|
15
|
+
|| !Object.hasOwn(probe, 'nonce') || !['activity.ping', 'activity.pong'].includes(probe.type)
|
|
16
|
+
|| probe.version !== 1 || typeof probe.nonce !== 'string' || !/^[A-Za-z0-9_-]{1,64}$/.test(probe.nonce))
|
|
17
|
+
return null;
|
|
18
|
+
return probe;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=activityProbe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activityProbe.js","sourceRoot":"","sources":["../../src/shared/activityProbe.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAC;AAClD,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAQ9C,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,qBAAqB;QAAE,OAAO,IAAI,CAAC;IAC1H,IAAI,CAAC;QACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7E,MAAM,KAAK,GAAG,KAAsB,CAAC;QACrC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC;eACnG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;eAC1F,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACvH,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -225,6 +225,31 @@ export interface CodeServerStopRequest {
|
|
|
225
225
|
instanceId?: string;
|
|
226
226
|
}
|
|
227
227
|
export type AccountProvider = 'codex' | 'claude';
|
|
228
|
+
export interface AccountRefreshStatus {
|
|
229
|
+
state: 'success' | 'retrying' | 'reauth_required' | 'deferred';
|
|
230
|
+
lastAttemptAt: string | null;
|
|
231
|
+
lastSuccessAt: string | null;
|
|
232
|
+
nextRetryAt: string | null;
|
|
233
|
+
code: string | null;
|
|
234
|
+
httpStatus: number | null;
|
|
235
|
+
}
|
|
236
|
+
export interface AccountUsage {
|
|
237
|
+
status: 'available' | 'unavailable' | 'reauth_required' | 'unsupported';
|
|
238
|
+
checkedAt: string;
|
|
239
|
+
code: string | null;
|
|
240
|
+
windows: Array<{
|
|
241
|
+
kind: 'weekly' | 'session';
|
|
242
|
+
usedPercent: number;
|
|
243
|
+
resetsAt: string | null;
|
|
244
|
+
}>;
|
|
245
|
+
}
|
|
246
|
+
export interface AccountUsageResponse {
|
|
247
|
+
provider: AccountProvider;
|
|
248
|
+
profiles: Array<{
|
|
249
|
+
profileId: string;
|
|
250
|
+
usage: AccountUsage;
|
|
251
|
+
}>;
|
|
252
|
+
}
|
|
228
253
|
export interface AccountProfile {
|
|
229
254
|
id: string;
|
|
230
255
|
provider: AccountProvider;
|
|
@@ -233,6 +258,7 @@ export interface AccountProfile {
|
|
|
233
258
|
updatedAt: string;
|
|
234
259
|
expiresAt: string | null;
|
|
235
260
|
refreshTokenExpiresAt: string | null;
|
|
261
|
+
refresh?: AccountRefreshStatus;
|
|
236
262
|
}
|
|
237
263
|
export interface AccountRunningPane {
|
|
238
264
|
terminalId: TerminalId;
|
|
@@ -312,8 +338,27 @@ export interface TerminalTranscriptHistoryResponse {
|
|
|
312
338
|
cursor: string | null;
|
|
313
339
|
hasMore: boolean;
|
|
314
340
|
output: string[];
|
|
341
|
+
page?: {
|
|
342
|
+
identity: string;
|
|
343
|
+
snapshot: string;
|
|
344
|
+
/** Settled readable end, excluding any incomplete trailing JSON. */
|
|
345
|
+
snapshotSize: number;
|
|
346
|
+
snapshotMtime: number;
|
|
347
|
+
startOffset: number;
|
|
348
|
+
endOffset: number;
|
|
349
|
+
stableBefore: number;
|
|
350
|
+
/** Display-capped blocks in ascending source order, without archive trailer. */
|
|
351
|
+
records: Array<{
|
|
352
|
+
id: string;
|
|
353
|
+
startOffset: number;
|
|
354
|
+
endOffset: number;
|
|
355
|
+
block: string;
|
|
356
|
+
}>;
|
|
357
|
+
unchanged?: boolean;
|
|
358
|
+
};
|
|
315
359
|
}
|
|
316
360
|
export interface TerminalTranscriptHistoryRequest {
|
|
361
|
+
ifSnapshot?: string;
|
|
317
362
|
cursor?: string;
|
|
318
363
|
limit?: number;
|
|
319
364
|
maxLines?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/shared/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -16,7 +16,15 @@ export interface TerminalSummary {
|
|
|
16
16
|
agentStatus?: {
|
|
17
17
|
provider: 'codex' | 'claude';
|
|
18
18
|
sessionId?: string;
|
|
19
|
-
state: 'working' | 'done' | 'stopped' | 'idle' | 'unknown';
|
|
19
|
+
state: 'working' | 'finishing' | 'waiting' | 'done' | 'stopped' | 'idle' | 'unknown';
|
|
20
|
+
source?: 'hook' | 'transcript';
|
|
21
|
+
revision?: number;
|
|
22
|
+
epoch?: string;
|
|
23
|
+
completion?: {
|
|
24
|
+
id: string;
|
|
25
|
+
outcome: TerminalCompletionOutcome;
|
|
26
|
+
occurredAt: string;
|
|
27
|
+
};
|
|
20
28
|
};
|
|
21
29
|
agentCache?: {
|
|
22
30
|
source: {
|
package/docs/features.md
CHANGED
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
## Terminal Workspace
|
|
4
4
|
|
|
5
5
|
- Tabs and split panes for real host PTYs.
|
|
6
|
+
- Named workspaces per server, each with a default folder and its own tabs and panes. Workspaces can be renamed, reordered, and deleted (deleting closes their sessions).
|
|
6
7
|
- Per-server workspace state.
|
|
7
8
|
- Refresh and temporary WebSocket reconnects reattach to active PTYs.
|
|
8
9
|
- Soft close detaches terminals into restorable pills.
|
|
9
10
|
- Tab rename, drag reorder, and drag-to-split controls.
|
|
10
11
|
- Activity dots and completion badges show whether a tab is running, idle, exited, or finished in the background.
|
|
12
|
+
- Working indicators are green and unread completion indicators are blue across themes, including mobile and favorites.
|
|
11
13
|
- Detached terminal destroy uses a themed confirmation modal.
|
|
12
14
|
- Tab titles sync with tmux window names when tmux persistence is active.
|
|
15
|
+
- Favorite tabs: star a tab, then open the favorites dialog (`Ctrl+Alt+F` or the ★ button) in grid or list view. Favorites are stored on the main server, work across servers, can be reordered, and a favorite whose tab is gone is recreated in its saved folder.
|
|
16
|
+
- The pane header can open the pane's folder in code-server (browser VS Code). NTerminal downloads and verifies the code-server release into `~/.nterminal/addons/code-server` on first use (Linux x64/arm64, needs `curl` and `tar`) and stops idle instances.
|
|
13
17
|
|
|
14
18
|
## Server Management
|
|
15
19
|
|
|
@@ -20,6 +24,7 @@
|
|
|
20
24
|
- CPU, memory, disk, and swap percentages above 90% / 95% are highlighted with warning / danger tones.
|
|
21
25
|
- Settings includes a Servers section for checking npm package updates on each server.
|
|
22
26
|
- Update all runs secondary server updates in parallel first, then updates the main server last.
|
|
27
|
+
- The Server information button on each server card shows the server role, external IPv4, that server's favorite tabs, and its processes grouped by project folder with CPU and memory totals.
|
|
23
28
|
- Background update checks run hourly and badge Settings/Updates when an update is available.
|
|
24
29
|
|
|
25
30
|
## Security And Access
|
|
@@ -31,9 +36,18 @@
|
|
|
31
36
|
- Trusted networks can be added by exact IP or CIDR to skip the re-auth prompt.
|
|
32
37
|
- Security settings can revoke trusted devices, remove trusted networks, and disable TOTP.
|
|
33
38
|
|
|
39
|
+
## Provider Accounts
|
|
40
|
+
|
|
41
|
+
- Each server keeps its own encrypted Codex and Claude credential vault. Active credentials remain in the CLI's native file, not a second vault copy.
|
|
42
|
+
- Account dialogs show access-token expiry, weekly usage remaining, reset time, and the last usage check. Quota checks are read-only, cached briefly, and never swap the active credential file.
|
|
43
|
+
- Saved accounts renew independently, including while another account is running or a new login is unfinished. Refresh outcomes include the last attempt, success, retry time, and a redacted provider error code.
|
|
44
|
+
- NTerminal defers direct refresh of an active credential while a native CLI owns it. An idle conversation does not mean authentication activity has stopped; native-owner refresh delegation is not implemented yet.
|
|
45
|
+
- Expired access tokens and revoked refresh tokens are different states. A provider-revoked refresh token requires a new login, not repeated automatic renewal attempts.
|
|
46
|
+
- Optional [provider lifecycle hooks](provider-hooks.md) can be installed from each server's account dialog or `nterminal hooks`. They provide verified session/activity signals without replacing native CLI authentication.
|
|
47
|
+
|
|
34
48
|
## Terminal Appearance And Settings
|
|
35
49
|
|
|
36
|
-
- Settings is organized into
|
|
50
|
+
- Settings is organized into Appearance, Shortcuts, Notifications, Security, and Servers sections.
|
|
37
51
|
- The settings sidebar highlights the visible section and jumps directly to that section.
|
|
38
52
|
- Terminal font size is adjustable with a slider, stepper, numeric input, and reset button.
|
|
39
53
|
- Terminal font family can switch between MesloLGS NF, JetBrains Mono, and system monospace stacks.
|
|
@@ -48,6 +62,8 @@
|
|
|
48
62
|
- Supported Codex/Claude panes show a `View history` header button and can continue into archived transcript history.
|
|
49
63
|
- JSONL transcript history is loaded by cursor only when NTerminal can determine the session source unambiguously.
|
|
50
64
|
- 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.
|
|
65
|
+
- Server-side search scans from newest to oldest, including records not yet loaded; selecting a result loads the surrounding history. See [Terminal History](terminal-history.md#search).
|
|
66
|
+
- `View agents` (`Alt+A`) lists the subagents a Codex or Claude session launched, with their state, and opens each subagent's conversation. Nested subagents fold under their parent.
|
|
51
67
|
|
|
52
68
|
## Files
|
|
53
69
|
|
|
@@ -129,6 +145,10 @@ NTerminal uses Control-based workspace shortcuts so browser-reserved Command sho
|
|
|
129
145
|
| Select pane by screen order | `Ctrl+Alt+Shift+1` through `Ctrl+Alt+Shift+9` |
|
|
130
146
|
| Split right | `Ctrl+Alt+Shift+Right` |
|
|
131
147
|
| Split down | `Ctrl+Alt+Shift+Down` |
|
|
148
|
+
| View transcript history | `Alt+H` |
|
|
149
|
+
| View subagents | `Alt+A` |
|
|
150
|
+
| Toggle tmux scrollback | `Alt+S` |
|
|
151
|
+
| Open favorite tabs | `Ctrl+Alt+F` |
|
|
132
152
|
|
|
133
153
|
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.
|
|
134
154
|
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Provider Lifecycle Hooks
|
|
2
|
+
|
|
3
|
+
Lifecycle hooks are optional, per runtime user and per server. They improve
|
|
4
|
+
Codex/Claude session identification and activity notifications without forwarding
|
|
5
|
+
terminal output or changing the conversation. Existing transcript/process
|
|
6
|
+
detection remains available when hooks are absent or a binding is ambiguous.
|
|
7
|
+
|
|
8
|
+
## Enable
|
|
9
|
+
|
|
10
|
+
Open the server's Codex or Claude account dialog, then choose **Install hooks**
|
|
11
|
+
and confirm. Main and secondary servers use the same control, including outbound
|
|
12
|
+
secondaries. Preview cannot change provider configuration.
|
|
13
|
+
|
|
14
|
+
The equivalent commands, run as the NTerminal runtime user, are:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
nterminal hooks status --provider codex
|
|
18
|
+
nterminal hooks install --provider codex
|
|
19
|
+
nterminal hooks install --provider claude
|
|
20
|
+
nterminal hooks remove --provider codex
|
|
21
|
+
nterminal hooks remove --provider claude
|
|
22
|
+
nterminal hooks trust --provider codex # asks for confirmation; --yes skips it
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Codex runs a hook from `hooks.json` only after you trust it. You can do that in
|
|
26
|
+
Codex (open `/hooks` and trust each NTerminal hook), or let NTerminal ask Codex
|
|
27
|
+
to do the same thing in one step:
|
|
28
|
+
|
|
29
|
+
- In the Codex account dialog, choose **Trust NTerminal hooks in Codex** and
|
|
30
|
+
confirm, or run `nterminal hooks trust --provider codex`.
|
|
31
|
+
- NTerminal starts a private, short-lived `codex app-server` over stdio, with
|
|
32
|
+
the same `CODEX_HOME` the hooks were installed into. It does not connect to or
|
|
33
|
+
start the shared Codex daemon, and remote control is off for that process.
|
|
34
|
+
The `codex` it runs is the first one on the runtime's `PATH` (then
|
|
35
|
+
`~/.npm-global/bin`, `~/.local/bin`, `/usr/local/bin`, `/opt/homebrew/bin`).
|
|
36
|
+
- Codex lists its hooks (`hooks/list`). NTerminal selects only the hooks that
|
|
37
|
+
come from its own `hooks.json`, are not managed by policy, and whose event,
|
|
38
|
+
command, timeout, and `async` flag exactly match what NTerminal installed and
|
|
39
|
+
recorded. If any NTerminal hook is missing, edited, or duplicated, nothing is
|
|
40
|
+
trusted.
|
|
41
|
+
- For those hooks only, Codex writes the trust itself (`config/batchWrite`),
|
|
42
|
+
exactly as pressing Trust in `/hooks` does: one
|
|
43
|
+
`[hooks.state."<hooks.json>:<event>:<group>:<handler>"]` table with
|
|
44
|
+
`trusted_hash` per hook in your `config.toml`. Codex computes the hash and
|
|
45
|
+
keeps the rest of the file. Other hooks in the same file stay untrusted.
|
|
46
|
+
- NTerminal then lists the hooks again and reports how many were trusted,
|
|
47
|
+
already trusted, or left unchanged, and fails with `verification_failed` if
|
|
48
|
+
Codex does not report every NTerminal hook as trusted.
|
|
49
|
+
- The whole exchange is limited to 10 seconds, and the Codex process (with any
|
|
50
|
+
helper it started) is always stopped afterwards. Preview refuses, and the
|
|
51
|
+
operation waits for no other hook installation: it uses the same lock.
|
|
52
|
+
|
|
53
|
+
Trusted hooks run outside the Codex sandbox; the command being trusted is the
|
|
54
|
+
NTerminal hook helper shown below. New Codex sessions (and resumed ones) read
|
|
55
|
+
the trust when they start, including sessions served by the shared daemon.
|
|
56
|
+
Sessions already running keep their hooks until they are reopened.
|
|
57
|
+
|
|
58
|
+
The dialog also asks Codex, read-only, whether the hooks are trusted. That
|
|
59
|
+
check runs at most once while `hooks.json`, `config.toml`, and NTerminal's
|
|
60
|
+
manifest are unchanged, and the result is reused by the status.
|
|
61
|
+
|
|
62
|
+
Errors: `codex_unavailable` (no `codex` found), `codex_timeout`,
|
|
63
|
+
`codex_rpc_failed` (Codex refused the request or its output was malformed),
|
|
64
|
+
`hooks_not_installed`, `hooks_not_listed` (Codex did not list every NTerminal
|
|
65
|
+
hook exactly as installed), `hooks_disabled`, `installation_busy`, and
|
|
66
|
+
`verification_failed`. In each case `/hooks` in Codex still works.
|
|
67
|
+
|
|
68
|
+
NTerminal never computes a trust hash itself or bypasses provider policy.
|
|
69
|
+
An unchanged hook definition retains its native trust;
|
|
70
|
+
an edited definition may require review again. Reinstalling or upgrading updates
|
|
71
|
+
NTerminal's hook groups in place, so their position (part of Codex's trust key)
|
|
72
|
+
and the position of your own hooks do not change. A new command (for example
|
|
73
|
+
after Node.js moved) still needs one review.
|
|
74
|
+
|
|
75
|
+
Configuration defaults are `~/.codex/hooks.json` and `~/.claude/settings.json`.
|
|
76
|
+
Runtime environment variables `CODEX_HOME` and `CLAUDE_CONFIG_DIR` override the
|
|
77
|
+
respective homes. Provider homes and settings files may be symlinks you own (for
|
|
78
|
+
example a dotfiles checkout); NTerminal edits the resolved file and keeps the
|
|
79
|
+
link. A symlink into a directory owned by another user or writable by others is
|
|
80
|
+
refused, and the error names that path. The state directory (`~/.nterminal`) may
|
|
81
|
+
also be a symlink. Provider policy can disable hooks even when the entries exist.
|
|
82
|
+
A configured hook is not necessarily active; the account dialog separately
|
|
83
|
+
reports whether verified events have been observed. The CLI reads the same
|
|
84
|
+
runtime `.env` as the launcher, including `export` assignments. Inherited
|
|
85
|
+
environment values take precedence; relative paths resolve from the application
|
|
86
|
+
directory, not the caller's working directory.
|
|
87
|
+
|
|
88
|
+
NTerminal never restarts a CLI to activate hooks. A CLI that does not reload its
|
|
89
|
+
configuration must be reopened by the operator. Use current provider versions
|
|
90
|
+
that support the installed events (Codex 0.158 and Claude Code 2.1.284 were
|
|
91
|
+
checked).
|
|
92
|
+
|
|
93
|
+
### What Gets Installed
|
|
94
|
+
|
|
95
|
+
One hook group per event, each running the same command:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
[ -x '<node>' ] && [ -f '<state>/hooks/forward.cjs' ] && exec '<node>' '<state>/hooks/forward.cjs' '--provider' '<provider>' '--endpoint' '<state>/hooks/endpoint.json' || exit 0
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`<state>` is the resolved (realpath) state directory. The command uses only
|
|
102
|
+
POSIX `[`, `exec`, and `exit`, which `sh`, `bash`, `zsh`, and `fish` accept
|
|
103
|
+
(Codex runs hooks with the user's shell, Claude with bash or `$SHELL`). If
|
|
104
|
+
Node.js moved (for example an `nvm` upgrade) or the state directory was deleted,
|
|
105
|
+
the hook exits 0 without output instead of showing a hook error. Reinstall to
|
|
106
|
+
point the hooks at the current Node.js.
|
|
107
|
+
|
|
108
|
+
- Codex: `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`,
|
|
109
|
+
`PermissionRequest`, `Stop`, `Interrupt`, `SessionEnd`.
|
|
110
|
+
- Claude: `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`,
|
|
111
|
+
`PostToolUseFailure`, `PermissionRequest`, `Notification`, `Stop`,
|
|
112
|
+
`StopFailure`, `SessionEnd`.
|
|
113
|
+
|
|
114
|
+
NTerminal never uses a hook's response, so tool, permission, notification, and
|
|
115
|
+
interrupt hooks are `async: true`: the CLI does not wait for them. Only the turn
|
|
116
|
+
and session boundaries (`SessionStart`, `UserPromptSubmit`, `Stop`,
|
|
117
|
+
`StopFailure`, `SessionEnd`) stay synchronous, bounded by a 1-second timeout.
|
|
118
|
+
Codex does not allow asynchronous `SessionEnd`, and a CLI may exit right after a
|
|
119
|
+
boundary event. Events carry the helper's start time, so an asynchronous tool
|
|
120
|
+
event that finishes late is still ordered before a later `Stop`. `Subagent*`
|
|
121
|
+
events are not registered because child lifecycle events do not change a pane's
|
|
122
|
+
status.
|
|
123
|
+
|
|
124
|
+
Codex `notify` is not used. `hooks.json` provides `Stop`, and the session's
|
|
125
|
+
transcript `task_complete` record confirms the turn within the regular status
|
|
126
|
+
poll. `notify` is a single slot that may belong to you, and Codex passes its JSON
|
|
127
|
+
as one command-line argument, which fails for payloads over 128 KiB. An existing
|
|
128
|
+
`notify` (root or `[profiles.*]`) is left untouched and reported as
|
|
129
|
+
`notify_foreign`. Earlier NTerminal versions installed a `# BEGIN/END NTerminal
|
|
130
|
+
notify` block in `config.toml`; reinstalling or removing deletes that block (it
|
|
131
|
+
is recognised even after Codex rewrote other parts of the file) and nothing else.
|
|
132
|
+
|
|
133
|
+
## Remove And Uninstall
|
|
134
|
+
|
|
135
|
+
`nterminal hooks remove --provider codex|claude` deletes only the hook groups
|
|
136
|
+
recorded in NTerminal's ownership manifest. Your own hooks and settings stay as
|
|
137
|
+
they were, including key order, indentation, and line endings. NTerminal also
|
|
138
|
+
deletes the Codex trust records (`[hooks.state."<hooks.json>:<event>:<group>:0"]`)
|
|
139
|
+
of the groups it removed, and nothing else in `config.toml`. A `hooks.json` or
|
|
140
|
+
`settings.json` that NTerminal created is deleted when it becomes empty. When
|
|
141
|
+
NTerminal's group is removed, your hooks listed after it in the same event move
|
|
142
|
+
up one position; Codex may then ask you to review them again.
|
|
143
|
+
|
|
144
|
+
`nterminal clean --force` and `nterminal uninstall --force` first remove
|
|
145
|
+
NTerminal's hooks from both providers. This is best effort: a failure prints a
|
|
146
|
+
warning and the command continues. Run `nterminal hooks status --provider
|
|
147
|
+
<provider>` afterwards if a warning appears. Uninstall NTerminal's hooks before
|
|
148
|
+
`npm uninstall -g nterminal`, because the hooks command is part of the package.
|
|
149
|
+
|
|
150
|
+
## Troubleshooting
|
|
151
|
+
|
|
152
|
+
`nterminal hooks status --provider <provider>` prints `reason` and `warnings`:
|
|
153
|
+
|
|
154
|
+
- `node_unavailable`: the recorded Node.js no longer exists. The hooks exit 0
|
|
155
|
+
silently; reinstall to use the current Node.js.
|
|
156
|
+
- `helper_missing`: `<state>/hooks/forward.cjs` is gone. Reinstall.
|
|
157
|
+
- `unsafe_path` with `unsafePath`: that path is a symlink to a location NTerminal
|
|
158
|
+
will not edit, or a symlink inside the state directory.
|
|
159
|
+
- `hooks_disabled`: hooks are off in `config.toml` (`[features] hooks = false`,
|
|
160
|
+
also in the active `profile`), in `/etc/codex/requirements.toml` or
|
|
161
|
+
`/etc/codex/managed_config.toml` (`allow_managed_hooks_only`, `features`), in
|
|
162
|
+
`settings.json` (`disableAllHooks`, `allowManagedHooksOnly`), or in Claude's
|
|
163
|
+
managed `/etc/claude-code/managed-settings.json` (and
|
|
164
|
+
`managed-settings.d/*.json`).
|
|
165
|
+
- `ownership_conflict`: an NTerminal hook group or legacy notify command was
|
|
166
|
+
edited. NTerminal does not adopt edited entries; restore or delete them by
|
|
167
|
+
hand.
|
|
168
|
+
- Warnings: `legacy_definitions` (installed by an earlier version; reinstall to
|
|
169
|
+
get asynchronous tool hooks and the guarded command), `notify_foreign`,
|
|
170
|
+
`notify_profile_override` (a profile's `notify` overrides NTerminal's legacy
|
|
171
|
+
notify), `profile_hooks_disabled` (a non-default Codex profile disables hooks),
|
|
172
|
+
and `managed_policy_unreadable`.
|
|
173
|
+
|
|
174
|
+
Not checked: Claude project settings (`.claude/settings.json` and
|
|
175
|
+
`.claude/settings.local.json` in a repository) can disable hooks for that
|
|
176
|
+
project. Codex hook trust is reported only after a trust check or trust
|
|
177
|
+
operation (see [Enable](#enable)); `approval_required` then means Codex lists
|
|
178
|
+
NTerminal hooks that still need approval, and `approval_unknown` that no report
|
|
179
|
+
is available.
|
|
180
|
+
|
|
181
|
+
## State And Notifications
|
|
182
|
+
|
|
183
|
+
- Idle retains the theme's existing idle color.
|
|
184
|
+
- Working is green; completion awaiting confirmation also stays active.
|
|
185
|
+
- Unread completion is blue in tabs, workspaces, and favorites. Displaying the
|
|
186
|
+
completed pane clears its unread mark. On mobile, other split panes remain
|
|
187
|
+
unread until displayed; focusing an editor does not mark a hidden terminal
|
|
188
|
+
read. A background server's last selected tab is not considered visible.
|
|
189
|
+
- Permission/input waits are not successful completion.
|
|
190
|
+
- A Codex `Stop` is only a completion candidate. The rollout's matching
|
|
191
|
+
`task_complete` marker confirms the turn on the next status poll (about 2 s).
|
|
192
|
+
Installs from earlier NTerminal versions may still send the legacy `notify`
|
|
193
|
+
`agent-turn-complete`, which is honored as confirmation too; current installs
|
|
194
|
+
no longer add a `notify` command.
|
|
195
|
+
- A Claude `Stop` is also provisional because another hook can continue the
|
|
196
|
+
turn. A matching post-turn `turn_duration` record confirms it when no pending
|
|
197
|
+
background agents/workflows remain. The record shape was checked against
|
|
198
|
+
Claude Code 2.1.283. A subsequent native idle notification can also confirm a
|
|
199
|
+
pending Stop. Missing/unknown evidence is not turned into success by a timer.
|
|
200
|
+
- A lost prompt hook can recover from a later Stop and fresh post-turn evidence.
|
|
201
|
+
A competing still-active turn is not replaced by anonymous evidence; it stays
|
|
202
|
+
pending until its identity can be verified. A failure recorded for that
|
|
203
|
+
candidate remains a stopped outcome when the identity is later confirmed.
|
|
204
|
+
- Errors and interruption produce a stopped outcome, not successful completion.
|
|
205
|
+
- Child/background hooks do not finish the parent session. A completed foreground
|
|
206
|
+
turn does not imply that a long-running goal or every detached task is finished.
|
|
207
|
+
|
|
208
|
+
Completion IDs are shared by live status, unread badges, animation notifications,
|
|
209
|
+
and push routing. The activity stream works independently of an open pane socket.
|
|
210
|
+
Reconnects replay a bounded recent metadata journal. A journal gap or server
|
|
211
|
+
restart sends a non-notifying snapshot instead of replaying historical alarms.
|
|
212
|
+
Missing entries in an activity snapshot never imply a terminal exit: remote
|
|
213
|
+
snapshots can be retained during an outage. Explicit terminal lifecycle events
|
|
214
|
+
and terminal discovery remain responsible for that state.
|
|
215
|
+
Initial REST loading preserves newer live state, local layout edits, and explicit
|
|
216
|
+
terminal deletion. It does not generate historical completion notifications.
|
|
217
|
+
|
|
218
|
+
The browser checks the separate activity connection while visible and on resume.
|
|
219
|
+
An unresponsive connection is replaced without reconnecting terminal output,
|
|
220
|
+
replaying input, resizing panes, or changing keyboard focus. These checks cannot
|
|
221
|
+
run while the browser suspends JavaScript; server-side push remains independent.
|
|
222
|
+
|
|
223
|
+
## Ownership And Resource Limits
|
|
224
|
+
|
|
225
|
+
The installed helper is kept under the persistent NTerminal state directory,
|
|
226
|
+
outside the npm package. Updates refresh only a verified, previously opted-in
|
|
227
|
+
helper; provider configuration and approval definitions are not silently changed.
|
|
228
|
+
Removal deletes only entries whose ownership still matches the installation
|
|
229
|
+
manifest. Concurrent edits and unknown existing commands fail closed.
|
|
230
|
+
Interrupted installations retain an ownership journal. Installer locks are
|
|
231
|
+
reclaimed only after verifying a dead owner; PID reuse, boot identity, and foreign
|
|
232
|
+
hosts are checked. Without a stable machine ID, recovery is limited to the same
|
|
233
|
+
boot. Unverifiable old locks are left intact, not removed based on age.
|
|
234
|
+
|
|
235
|
+
Helper upgrades and rollback use one durable ownership journal shared by both
|
|
236
|
+
providers. A failed configuration rollback retains any helper that a remaining
|
|
237
|
+
hook may reference. User-modified helper files are never silently adopted.
|
|
238
|
+
|
|
239
|
+
The receiver also recovers publication locks whose recorded owner is provably
|
|
240
|
+
dead. An old, empty `hooks/.endpoint-lock` directory has no ownership record and
|
|
241
|
+
cannot be recovered automatically. If that specific legacy marker blocks hook
|
|
242
|
+
activation, stop every runtime sharing the state directory, verify the marker
|
|
243
|
+
is an empty directory, and remove only that empty directory with `rmdir` before
|
|
244
|
+
starting again. Do not delete ownership records or a lock held by a running
|
|
245
|
+
runtime. Ordinary transcript/process detection remains available when hook
|
|
246
|
+
activation is unavailable.
|
|
247
|
+
|
|
248
|
+
Events are sent through a private Unix socket, authenticated with a runtime
|
|
249
|
+
capability. The receiver checks the live helper/provider process ancestry and
|
|
250
|
+
process start identities. A session is assigned to a pane using verified process
|
|
251
|
+
ownership or an exact native binding, never just the working directory. Shared
|
|
252
|
+
daemon ambiguity falls back to native discovery/manual history selection rather
|
|
253
|
+
than assigning another pane's session. Claude hooks run as children of the
|
|
254
|
+
pane's Claude process, so their binding is exact. Codex needs the extra rules in
|
|
255
|
+
[Codex Shared Background Server](#codex-shared-background-server).
|
|
256
|
+
A transient discovery gap does not retire an existing hook session. Old resume
|
|
257
|
+
arguments cannot replace it, and process ownership is rechecked after asynchronous
|
|
258
|
+
validation before publishing status or completion.
|
|
259
|
+
Nested provider processes do not take over the parent pane's binding, including
|
|
260
|
+
children sharing its foreground process group. Normal Codex npm launchers and
|
|
261
|
+
provider commands that replace the pane shell remain supported.
|
|
262
|
+
|
|
263
|
+
Transcript identity checks use 64 KiB chunks with a 512 KiB header limit, not a
|
|
264
|
+
whole-history read. A bounded cache retains identity proofs rather than message
|
|
265
|
+
text; changed files require prefix revalidation. Oversized headers need an exact
|
|
266
|
+
live writable-file association. Incomplete, conflicting, or unprovable fork
|
|
267
|
+
metadata stays unconfirmed instead of selecting a different conversation.
|
|
268
|
+
|
|
269
|
+
Only lifecycle metadata is forwarded. Prompts, tool arguments, assistant bodies,
|
|
270
|
+
credentials, and tokens are not included in event payloads or diagnostic logs.
|
|
271
|
+
Queues, messages, sockets, journal size, and helper duration are bounded. Hook
|
|
272
|
+
failures return neutral output so NTerminal cannot block or steer the native CLI.
|
|
273
|
+
Under load, per-tool events (`PreToolUse`/`PostToolUse`, synchronous or
|
|
274
|
+
asynchronous) are coalesced per session and dropped first. Start, stop, and
|
|
275
|
+
completion events use a reserved part of the queue and evict waiting tool
|
|
276
|
+
events, so a `Stop` is not refused while tool events are queued. Tool events for
|
|
277
|
+
a session no pane is bound to never trigger a Codex daemon lookup. A failed
|
|
278
|
+
lookup for a session is reused for 5 seconds unless a `SessionStart` or
|
|
279
|
+
`UserPromptSubmit` for it arrives.
|
|
280
|
+
JSONL remains the source for history, token usage, and completion reconciliation;
|
|
281
|
+
hook-backed terminals use a slower usage refresh and a small cached tail check
|
|
282
|
+
while confirming completion.
|
|
283
|
+
|
|
284
|
+
## Codex Shared Background Server
|
|
285
|
+
|
|
286
|
+
Codex 0.158 hands every interactive session to one shared background server
|
|
287
|
+
(`codex app-server --managed-daemon`). Its hooks run as children of that
|
|
288
|
+
server, not of the pane, so process ancestry cannot identify the pane. NTerminal
|
|
289
|
+
instead follows each pane's Codex connection to the server and matches the
|
|
290
|
+
server's open sessions by working directory. That match alone is not enough to
|
|
291
|
+
bind a session to a pane, because another Codex client, such as another
|
|
292
|
+
terminal or VS Code, can have a session open in the same directory.
|
|
293
|
+
|
|
294
|
+
A new Codex binding therefore also needs evidence tied to that pane's Codex
|
|
295
|
+
process:
|
|
296
|
+
|
|
297
|
+
- the first binding after Codex starts: `codex resume <id>` names the session,
|
|
298
|
+
or the session was created while that Codex process was starting (Codex
|
|
299
|
+
session IDs record their creation time);
|
|
300
|
+
- a later switch in the same process (`/new`, `/resume`): the session's
|
|
301
|
+
`SessionStart` arrived after the pane's last bound event;
|
|
302
|
+
- or, at any time, the pane's Codex is the only client of the shared server in
|
|
303
|
+
that directory: every other connected client is an identified Codex process
|
|
304
|
+
working in a different directory. This covers sessions opened from the
|
|
305
|
+
`codex resume` picker (no ID in the command line) and switches whose
|
|
306
|
+
`SessionStart` was missed. A connected client that cannot be identified as
|
|
307
|
+
Codex, or whose directory cannot be read, disables this rule.
|
|
308
|
+
|
|
309
|
+
A session already bound to one pane is never moved to another pane. If several
|
|
310
|
+
panes qualify, none is bound. A binding created by a completion event does not
|
|
311
|
+
notify for that turn, because NTerminal did not see the turn start.
|
|
312
|
+
|
|
313
|
+
Limits:
|
|
314
|
+
|
|
315
|
+
- **One Codex per directory.** Hook binding needs one Codex session per working
|
|
316
|
+
directory on the shared server. With two sessions in the same directory, both
|
|
317
|
+
panes stay unbound. The account dialog lists these panes as not linked to hook
|
|
318
|
+
events, and they keep transcript-based status. To run several Codex sessions
|
|
319
|
+
in one directory with hooks, start them with `codex --no-daemon` (also
|
|
320
|
+
`codex resume --no-daemon <id>`). That runs the session inside the pane's own
|
|
321
|
+
Codex process, where binding is exact.
|
|
322
|
+
- **About 60 seconds after `/new` or `/resume`.** The server keeps the previous
|
|
323
|
+
session loaded for about a minute. Until it unloads, the pane has two
|
|
324
|
+
sessions in the same directory and stays on its previous binding. Hook status
|
|
325
|
+
resumes with the next prompt or stop event after that. Codex also creates a
|
|
326
|
+
new session's file only when the session is first saved, so a new session has
|
|
327
|
+
nothing to match until its first prompt.
|
|
328
|
+
- **After an NTerminal restart.** A Codex pane that switched sessions with
|
|
329
|
+
`/new` or `/resume` before the restart binds again once it is the only
|
|
330
|
+
client in its directory; otherwise it keeps transcript-based status until its
|
|
331
|
+
next `/new` or `/resume`.
|
|
332
|
+
- **Clients outside NTerminal.** Other connected Codex clients, inside or
|
|
333
|
+
outside NTerminal, are ruled out by directory. A client that has already
|
|
334
|
+
disconnected while its turn keeps running on the server, in the same
|
|
335
|
+
directory as a pane whose own session is not saved yet, cannot be told
|
|
336
|
+
apart. One Codex per directory, or `--no-daemon`, avoids this.
|
|
337
|
+
|
|
338
|
+
The local receiver currently requires Linux procfs and Unix sockets. Unsupported
|
|
339
|
+
environments retain transcript/process detection. This integration does not
|
|
340
|
+
change credential-switch or credential-refresh safety rules.
|
package/docs/terminal-history.md
CHANGED
|
@@ -45,10 +45,20 @@ JSONL records are loaded by cursor and rendered into readable user, assistant, a
|
|
|
45
45
|
|
|
46
46
|
Loading older pages keeps previously loaded pages in the dialog, so ordinary scrolling can return to recent conversations without fetching them again.
|
|
47
47
|
|
|
48
|
+
Closing History retains its conversation pages and reading position in a browser-memory cache. Reopening the same server/terminal/session restores them immediately while checking the latest source snapshot. Unchanged sources need no conversation payload. If output was appended, bounded backward pages bridge the previous stable prefix before replacing the mutable tail; disconnected ranges are never joined as if contiguous. Source replacement or truncation resets the snapshot. Text selection defers applying refreshed content. Search-result excerpts are kept separate from the browsing snapshot.
|
|
49
|
+
|
|
50
|
+
The browser cache is cleared on logout or authentication loss, and late requests cannot refill it. Its 64 MiB approximate text budget and 30-minute idle expiry reclaim closed readers on cache activity, never truncate an active reader. Nothing is written to localStorage or IndexedDB. A full page reload therefore still fetches History from the server's disk cache.
|
|
51
|
+
|
|
48
52
|
### Derived Conversation Cache
|
|
49
53
|
|
|
50
54
|
History and search share private derived files below the state directory's `transcript-cache` directory. Recent records are returned first. After a user opens or searches a session, a background worker fills the remaining archive, one approximately 1 MiB source window at a time with a pause between windows. Foreground requests take priority over subsequent background work. Completed archives are checked for changes every 30 seconds; appended JSONL output updates the tail while keeping valid older ranges. Source replacement, truncation, or a parser version change invalidates affected cache data. Previously visited sources are remembered in private cache metadata so prebuilding resumes after a server restart.
|
|
51
55
|
|
|
56
|
+
Source extraction windows are independent of storage size. Background compaction merges adjacent stable segments toward 1 MiB of derived data, retaining original offsets and empty coverage. Mutable tail segments are excluded. Merges require balanced input sizes so small appends cannot repeatedly rewrite a large segment. Each pass handles at most 16 files and yields at file boundaries when foreground work arrives; deferred merges remain pending rather than being marked complete. New files are published through an atomic manifest replacement before old files are removed; existing cache data is reused without reparsing the JSONL. Read requests reuse parsed segments, avoid permission writes when modes are already private, and do not rewrite unchanged manifests. Access timestamps are persisted at most once per minute. Fragmented caches limit a page to 32 windows and retain a continuation cursor rather than performing unbounded small-file I/O.
|
|
57
|
+
|
|
58
|
+
Files retired by invalidation or compaction are reclaimed by the bounded background maintenance scan, not by the request that replaces the manifest. They remain included in disk usage until cleanup accounts for their deletion. A parser upgrade therefore does not make the first History request wait for thousands of old cache files to be deleted.
|
|
59
|
+
|
|
60
|
+
Parser v3 bounds Codex duplicate evidence to the same 64 KiB context used for stable-tail invalidation. Forward distances use the ends of the JSON payloads, excluding trailing whitespace and line delimiters, so a bounded read or a later newline cannot change the result. Duplicate evidence outside that range remains visible instead of changing old content depending on page size or future appends. Existing v2 Codex cache data is regenerated once for this semantic correction; v2 Claude records are reused without re-extraction.
|
|
61
|
+
|
|
52
62
|
The cache survives an NTerminal restart and uses private directory/file permissions. There is no session-count or per-session size quota. Maintenance expires caches seven days after the last user History or search request, and evicts least recently used caches at an approximately 5 GiB total safety limit. Background activity does not renew retention. New writes pause when less than 512 MiB of disk space is available; cached reads and original JSONL reads remain available. Maintenance normally runs every five minutes, and also when the total limit is reached. It contains conversation text and source paths, so treat the state directory as sensitive. Source JSONL files remain authoritative and are never changed. Missing or invalid cache files are rebuilt on demand.
|
|
53
63
|
|
|
54
64
|
Cache storage failures fall back to reading the source JSONL. Search continuation does not depend on the cache remaining available. Search text is extracted before display truncation; a result deep inside a long message opens an excerpt around the match.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nterminal",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"license": "GPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@10.9.7",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"docs/",
|
|
23
23
|
"public/",
|
|
24
24
|
"scripts/nterminalctl",
|
|
25
|
+
"scripts/nterminal-hook.cjs",
|
|
25
26
|
"scripts/nterminal-tmux.conf"
|
|
26
27
|
],
|
|
27
28
|
"scripts": {
|