nterminal 1.2.78 → 1.2.80
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/SECURITY.md +6 -0
- package/dist/client/assets/MarkdownPreview-D2UZ6wJU.js +1 -0
- package/dist/client/assets/{TranscriptMarkdownBody-BkGu6YVq.js → TranscriptMarkdownBody-BFu2FzgT.js} +1 -1
- package/dist/client/assets/index-4MeJRcb6.js +57 -0
- package/dist/client/assets/{index-Bgqck-vt.js → index-BFs4imuQ.js} +1 -1
- package/dist/client/assets/index-rROkcwjD.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/scripts/onboarding.js +10 -1
- package/dist/scripts/onboarding.js.map +1 -1
- package/dist/server/agent/agentProxy.d.ts +7 -2
- package/dist/server/agent/agentProxy.js +75 -9
- package/dist/server/agent/agentProxy.js.map +1 -1
- package/dist/server/agent/agentRoutes.js +159 -33
- package/dist/server/agent/agentRoutes.js.map +1 -1
- package/dist/server/agent/agentWebSocketProxy.d.ts +10 -1
- package/dist/server/agent/agentWebSocketProxy.js +24 -4
- package/dist/server/agent/agentWebSocketProxy.js.map +1 -1
- package/dist/server/auth/authService.d.ts +18 -4
- package/dist/server/auth/authService.js +57 -6
- package/dist/server/auth/authService.js.map +1 -1
- package/dist/server/eventLoopDelayMonitor.d.ts +11 -0
- package/dist/server/eventLoopDelayMonitor.js +37 -0
- package/dist/server/eventLoopDelayMonitor.js.map +1 -0
- package/dist/server/http.js +11 -4
- package/dist/server/http.js.map +1 -1
- package/dist/server/index.js +18 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/agentManagementRoutes.d.ts +2 -1
- package/dist/server/routes/agentManagementRoutes.js +91 -37
- package/dist/server/routes/agentManagementRoutes.js.map +1 -1
- package/dist/server/routes/authRoutes.js +4 -1
- package/dist/server/routes/authRoutes.js.map +1 -1
- package/dist/server/routes/authenticatedSocketRegistry.d.ts +25 -0
- package/dist/server/routes/authenticatedSocketRegistry.js +171 -0
- package/dist/server/routes/authenticatedSocketRegistry.js.map +1 -0
- package/dist/server/routes/codeServerRoutes.d.ts +3 -1
- package/dist/server/routes/codeServerRoutes.js +139 -26
- package/dist/server/routes/codeServerRoutes.js.map +1 -1
- package/dist/server/routes/fileRoutes.js +1 -1
- package/dist/server/routes/fileRoutes.js.map +1 -1
- package/dist/server/routes/httpProxyBody.d.ts +2 -1
- package/dist/server/routes/httpProxyBody.js +8 -1
- package/dist/server/routes/httpProxyBody.js.map +1 -1
- package/dist/server/routes/socketBackpressure.d.ts +2 -0
- package/dist/server/routes/socketBackpressure.js +21 -10
- package/dist/server/routes/socketBackpressure.js.map +1 -1
- package/dist/server/routes/terminalRoutes.d.ts +4 -3
- package/dist/server/routes/terminalRoutes.js +62 -14
- package/dist/server/routes/terminalRoutes.js.map +1 -1
- package/dist/server/routes/terminalSocketMessage.d.ts +34 -0
- package/dist/server/routes/terminalSocketMessage.js +188 -0
- package/dist/server/routes/terminalSocketMessage.js.map +1 -0
- package/dist/server/routes/terminalWebSocket.d.ts +3 -1
- package/dist/server/routes/terminalWebSocket.js +191 -116
- package/dist/server/routes/terminalWebSocket.js.map +1 -1
- package/dist/server/routes/webSocketRelay.d.ts +14 -0
- package/dist/server/routes/webSocketRelay.js +188 -18
- package/dist/server/routes/webSocketRelay.js.map +1 -1
- package/dist/server/routes/webSocketUpgradeAuth.d.ts +5 -0
- package/dist/server/routes/webSocketUpgradeAuth.js +21 -0
- package/dist/server/routes/webSocketUpgradeAuth.js.map +1 -0
- package/dist/server/system/stats.d.ts +1 -1
- package/dist/server/system/stats.js +11 -7
- package/dist/server/system/stats.js.map +1 -1
- package/dist/server/terminal/PtyAdapter.d.ts +25 -8
- package/dist/server/terminal/PtyAdapter.js +12 -1
- package/dist/server/terminal/PtyAdapter.js.map +1 -1
- package/dist/server/terminal/TerminalManager.d.ts +71 -13
- package/dist/server/terminal/TerminalManager.js +955 -163
- package/dist/server/terminal/TerminalManager.js.map +1 -1
- package/dist/server/terminal/TmuxPtyAdapter.d.ts +85 -15
- package/dist/server/terminal/TmuxPtyAdapter.js +1364 -403
- package/dist/server/terminal/TmuxPtyAdapter.js.map +1 -1
- package/dist/server/terminal/asyncCommand.d.ts +17 -0
- package/dist/server/terminal/asyncCommand.js +60 -0
- package/dist/server/terminal/asyncCommand.js.map +1 -0
- package/dist/server/terminal/codexTranscriptSource.d.ts +3 -3
- package/dist/server/terminal/codexTranscriptSource.js +90 -104
- package/dist/server/terminal/codexTranscriptSource.js.map +1 -1
- package/dist/server/terminal/outputBuffer.d.ts +5 -1
- package/dist/server/terminal/outputBuffer.js +29 -3
- package/dist/server/terminal/outputBuffer.js.map +1 -1
- package/dist/server/terminal/procfs.d.ts +7 -8
- package/dist/server/terminal/procfs.js +56 -47
- package/dist/server/terminal/procfs.js.map +1 -1
- package/dist/server/terminal/transcriptHistory.d.ts +1 -1
- package/dist/server/terminal/transcriptHistory.js +187 -38
- package/dist/server/terminal/transcriptHistory.js.map +1 -1
- package/dist/server/terminal/ttlCache.d.ts +1 -0
- package/dist/server/terminal/ttlCache.js +3 -0
- package/dist/server/terminal/ttlCache.js.map +1 -1
- package/dist/shared/protocol.d.ts +24 -0
- package/dist/shared/protocol.js +25 -2
- package/dist/shared/protocol.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/MarkdownPreview-D5NthhhG.js +0 -1
- package/dist/client/assets/index-B3_mYQzf.css +0 -1
- package/dist/client/assets/index-gSePR0Ob.js +0 -57
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync, readFileSync, readdirSync, readlinkSync } from 'node:fs';
|
|
1
|
+
import { access, readFile, readdir, readlink } from 'node:fs/promises';
|
|
3
2
|
import os from 'node:os';
|
|
4
3
|
import path from 'node:path';
|
|
5
4
|
import { isSessionId, sessionIdPatternSource } from './sessionId.js';
|
|
6
|
-
import {
|
|
5
|
+
import { readProcessStartedAtSecondsAsync } from './procfs.js';
|
|
7
6
|
import { TtlCache } from './ttlCache.js';
|
|
7
|
+
import { KeyedSingleFlight, mapWithConcurrency, runCommand } from './asyncCommand.js';
|
|
8
8
|
const PROCESS_START_SKEW_SECONDS = 2;
|
|
9
9
|
const LOOKUP_CACHE_SUCCESS_TTL_MS = 30_000;
|
|
10
10
|
// Must exceed the client's steady transcript-source poll cadence (15s in
|
|
11
11
|
// XtermPane); a shorter negative TTL guarantees every poll misses the cache
|
|
12
|
-
// and re-runs the
|
|
12
|
+
// and re-runs the sqlite lookup.
|
|
13
13
|
const LOOKUP_CACHE_FAILURE_TTL_MS = 20_000;
|
|
14
14
|
// The optional middle segment keeps this in sync with the loose
|
|
15
15
|
// startsWith('rollout-')/endsWith('<id>.jsonl') checks used elsewhere — a
|
|
@@ -17,65 +17,62 @@ const LOOKUP_CACHE_FAILURE_TTL_MS = 20_000;
|
|
|
17
17
|
// fd scan while passing the other matchers.
|
|
18
18
|
const CODEX_ROLLOUT_FILE_PATTERN = new RegExp(`^rollout-(?:.*-)?(${sessionIdPatternSource})\\.jsonl$`);
|
|
19
19
|
const processLogLookupCache = new TtlCache();
|
|
20
|
+
const processLogLookupInflight = new KeyedSingleFlight();
|
|
20
21
|
export function clearCodexTranscriptLookupCache() {
|
|
21
22
|
processLogLookupCache.clear();
|
|
23
|
+
processLogLookupInflight.clear();
|
|
22
24
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// /proc/<pid>/fd names the exact file — no sqlite, and it follows thread
|
|
26
|
-
// switches (/new, resume) the moment codex reopens a different rollout.
|
|
27
|
-
// Write access is required so that transient read-only opens (codex browsing
|
|
28
|
-
// other sessions' history) are never mistaken for the live transcript.
|
|
29
|
-
// /proc/<pid>/fd is same-uid readable, the same constraint as the
|
|
30
|
-
// /proc/<pid>/environ and /cwd reads the adapter already relies on.
|
|
31
|
-
export function resolveCodexTranscriptFromOpenFds(pid, options = {}) {
|
|
32
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
25
|
+
export async function resolveCodexTranscriptFromOpenFds(pid, options = {}) {
|
|
26
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
33
27
|
return null;
|
|
34
|
-
}
|
|
35
28
|
const procRoot = options.procRoot ?? '/proc';
|
|
36
29
|
const fdDir = path.join(procRoot, String(pid), 'fd');
|
|
37
30
|
let fdNames;
|
|
38
31
|
try {
|
|
39
|
-
fdNames =
|
|
32
|
+
fdNames = await readdir(fdDir);
|
|
40
33
|
}
|
|
41
34
|
catch {
|
|
42
35
|
return null;
|
|
43
36
|
}
|
|
44
|
-
const
|
|
45
|
-
for (const fdName of fdNames) {
|
|
46
|
-
let target;
|
|
37
|
+
const candidates = await mapWithConcurrency(fdNames, 16, async (fdName) => {
|
|
47
38
|
try {
|
|
48
|
-
target =
|
|
39
|
+
const target = await readlink(path.join(fdDir, fdName));
|
|
40
|
+
if (target.endsWith(' (deleted)'))
|
|
41
|
+
return null;
|
|
42
|
+
const match = CODEX_ROLLOUT_FILE_PATTERN.exec(path.basename(target));
|
|
43
|
+
if (!match || !(await isFdOpenForWritingAsync(procRoot, pid, fdName)))
|
|
44
|
+
return null;
|
|
45
|
+
return { sessionId: match[1], filePath: target };
|
|
49
46
|
}
|
|
50
47
|
catch {
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
if (target.endsWith(' (deleted)')) {
|
|
54
|
-
continue;
|
|
48
|
+
return null;
|
|
55
49
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
});
|
|
51
|
+
const openRollouts = new Map();
|
|
52
|
+
for (const candidate of candidates) {
|
|
53
|
+
if (candidate)
|
|
54
|
+
openRollouts.set(candidate.sessionId, candidate.filePath);
|
|
61
55
|
}
|
|
62
|
-
if (openRollouts.size !== 1)
|
|
56
|
+
if (openRollouts.size !== 1)
|
|
63
57
|
return null;
|
|
64
|
-
}
|
|
65
58
|
const [sessionId, filePath] = [...openRollouts.entries()][0];
|
|
66
|
-
|
|
59
|
+
try {
|
|
60
|
+
await access(filePath);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
67
63
|
return null;
|
|
68
64
|
}
|
|
69
65
|
return { provider: 'codex', sessionId, filePath };
|
|
70
66
|
}
|
|
71
|
-
function
|
|
72
|
-
let raw;
|
|
67
|
+
async function isFdOpenForWritingAsync(procRoot, pid, fdName) {
|
|
73
68
|
try {
|
|
74
|
-
|
|
69
|
+
return fdFlagsAllowWriting(await readFile(path.join(procRoot, String(pid), 'fdinfo', fdName), 'utf8'));
|
|
75
70
|
}
|
|
76
71
|
catch {
|
|
77
72
|
return false;
|
|
78
73
|
}
|
|
74
|
+
}
|
|
75
|
+
function fdFlagsAllowWriting(raw) {
|
|
79
76
|
const line = raw.split('\n').find((candidate) => candidate.startsWith('flags:'));
|
|
80
77
|
if (!line) {
|
|
81
78
|
return false;
|
|
@@ -87,79 +84,50 @@ function isFdOpenForWriting(procRoot, pid, fdName) {
|
|
|
87
84
|
const accessMode = flags & 0o3;
|
|
88
85
|
return accessMode === 0o1 || accessMode === 0o2;
|
|
89
86
|
}
|
|
90
|
-
export function resolveCodexTranscriptFromProcessLogs(pid, options = {}) {
|
|
91
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
87
|
+
export async function resolveCodexTranscriptFromProcessLogs(pid, options = {}) {
|
|
88
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
92
89
|
return null;
|
|
93
|
-
}
|
|
94
90
|
const homeDir = options.homeDir ?? os.homedir();
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
const logsDb = path.join(homeDir, '.codex', 'logs_2.sqlite');
|
|
92
|
+
const stateDb = path.join(homeDir, '.codex', 'state_5.sqlite');
|
|
93
|
+
try {
|
|
94
|
+
await Promise.all([access(logsDb), access(stateDb)]);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
99
97
|
return null;
|
|
100
98
|
}
|
|
101
99
|
const processStartedAtSeconds = options.processStartedAtSeconds !== undefined
|
|
102
100
|
? options.processStartedAtSeconds
|
|
103
|
-
:
|
|
104
|
-
if (processStartedAtSeconds === null)
|
|
101
|
+
: await readProcessStartedAtSecondsAsync(pid);
|
|
102
|
+
if (processStartedAtSeconds === null)
|
|
105
103
|
return null;
|
|
106
|
-
}
|
|
107
|
-
// The sqlite3 shell-out is bounded by a 1s kill timer and blocks the event
|
|
108
|
-
// loop while it runs, so successful and failed lookups are both cached.
|
|
109
|
-
// The db path is part of the key because it is what the lookup actually
|
|
110
|
-
// reads; pid + start time identify the process across pid reuse.
|
|
111
104
|
const cacheKey = `${logsDb}:${pid}:${processStartedAtSeconds}`;
|
|
112
105
|
const cached = processLogLookupCache.get(cacheKey);
|
|
113
|
-
if (cached)
|
|
106
|
+
if (cached)
|
|
114
107
|
return cached.value;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
return processLogLookupInflight.run(cacheKey, async () => {
|
|
109
|
+
const source = await lookupCodexTranscriptInProcessLogs(pid, logsDb, stateDb, processStartedAtSeconds, options.runSqlite);
|
|
110
|
+
processLogLookupCache.set(cacheKey, source, source ? LOOKUP_CACHE_SUCCESS_TTL_MS : LOOKUP_CACHE_FAILURE_TTL_MS);
|
|
111
|
+
return source;
|
|
112
|
+
});
|
|
119
113
|
}
|
|
120
|
-
function lookupCodexTranscriptInProcessLogs(pid, logsDb, stateDb, processStartedAtSeconds, runSqliteOverride) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// over the whole log table — measured 7.2s cold / 0.5s warm on a 63MB log
|
|
126
|
-
// db, routinely overrunning the 1s sqlite timeout.
|
|
127
|
-
//
|
|
128
|
-
// last_ts (the newest log this pid wrote into each thread) preserves the
|
|
129
|
-
// old query's recency semantics: a process that switched threads (/new)
|
|
130
|
-
// has logged into several, and the one it touched most recently is the
|
|
131
|
-
// live one. Requiring global uniqueness instead would go permanently
|
|
132
|
-
// ambiguous after the first /new.
|
|
133
|
-
const output = (runSqliteOverride ?? runSqlite)(logsDb, [
|
|
134
|
-
`ATTACH DATABASE ${sqlString(stateDb)} AS state;`,
|
|
135
|
-
'SELECT id || char(9) || rollout_path || char(9) || last_ts FROM (',
|
|
136
|
-
' SELECT t.id AS id, t.rollout_path AS rollout_path, (',
|
|
137
|
-
' SELECT MAX(l.ts) FROM logs l',
|
|
138
|
-
' WHERE l.thread_id = t.id',
|
|
139
|
-
` AND l.ts >= ${minLogSeconds}`,
|
|
140
|
-
` AND l.process_uuid LIKE ${sqlString(`pid:${pid}:%`)}`,
|
|
141
|
-
' ) AS last_ts',
|
|
142
|
-
' FROM state.threads t',
|
|
143
|
-
" WHERE t.source = 'cli'",
|
|
144
|
-
" AND t.model_provider = 'openai'",
|
|
145
|
-
' AND t.rollout_path IS NOT NULL',
|
|
146
|
-
')',
|
|
147
|
-
'WHERE last_ts IS NOT NULL',
|
|
148
|
-
'ORDER BY last_ts DESC',
|
|
149
|
-
'LIMIT 50;'
|
|
150
|
-
].join('\n'));
|
|
151
|
-
if (output === null) {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
114
|
+
async function lookupCodexTranscriptInProcessLogs(pid, logsDb, stateDb, processStartedAtSeconds, runSqliteOverride) {
|
|
115
|
+
const output = await runProcessLogQuery(pid, logsDb, stateDb, processStartedAtSeconds, runSqliteOverride);
|
|
116
|
+
return output === null ? null : parseProcessLogMatchesAsync(output);
|
|
117
|
+
}
|
|
118
|
+
async function parseProcessLogMatchesAsync(output) {
|
|
154
119
|
const matches = new Map();
|
|
155
120
|
for (const line of output.split('\n')) {
|
|
156
121
|
const trimmed = line.trim();
|
|
157
122
|
if (!trimmed)
|
|
158
123
|
continue;
|
|
159
124
|
const [sessionId, filePath, lastTsRaw] = trimmed.split('\t');
|
|
160
|
-
if (!sessionId || !filePath)
|
|
125
|
+
if (!sessionId || !filePath || !isSessionId(sessionId) || !isCodexRolloutPath(filePath, sessionId))
|
|
161
126
|
continue;
|
|
162
|
-
|
|
127
|
+
try {
|
|
128
|
+
await access(filePath);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
163
131
|
continue;
|
|
164
132
|
}
|
|
165
133
|
const lastLogSeconds = Number(lastTsRaw);
|
|
@@ -169,28 +137,46 @@ function lookupCodexTranscriptInProcessLogs(pid, logsDb, stateDb, processStarted
|
|
|
169
137
|
matches.set(sessionId, { filePath, lastLogSeconds: normalized });
|
|
170
138
|
}
|
|
171
139
|
}
|
|
172
|
-
if (matches.size === 0)
|
|
140
|
+
if (matches.size === 0)
|
|
173
141
|
return null;
|
|
174
|
-
}
|
|
175
142
|
const ranked = [...matches.entries()].sort((a, b) => b[1].lastLogSeconds - a[1].lastLogSeconds);
|
|
176
|
-
if (ranked.length > 1 && ranked[0][1].lastLogSeconds <= ranked[1][1].lastLogSeconds)
|
|
177
|
-
// Two threads with equally recent activity (1s log granularity) — there
|
|
178
|
-
// is no safe winner, so refuse rather than guess.
|
|
143
|
+
if (ranked.length > 1 && ranked[0][1].lastLogSeconds <= ranked[1][1].lastLogSeconds)
|
|
179
144
|
return null;
|
|
180
|
-
}
|
|
181
145
|
const [sessionId, entry] = ranked[0];
|
|
182
146
|
return { provider: 'codex', sessionId, filePath: entry.filePath };
|
|
183
147
|
}
|
|
184
|
-
function
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
148
|
+
async function runProcessLogQuery(pid, logsDb, stateDb, processStartedAtSeconds, runSqliteOverride) {
|
|
149
|
+
const minLogSeconds = Math.max(0, Math.floor(processStartedAtSeconds) - PROCESS_START_SKEW_SECONDS);
|
|
150
|
+
const sql = processLogSql(pid, stateDb, minLogSeconds);
|
|
151
|
+
if (runSqliteOverride) {
|
|
152
|
+
return await runSqliteOverride(logsDb, sql);
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
return (await runCommand('sqlite3', ['-batch', '-noheader', logsDb, sql], { timeoutMs: 1_000 })).stdout;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
191
158
|
return null;
|
|
192
159
|
}
|
|
193
|
-
|
|
160
|
+
}
|
|
161
|
+
function processLogSql(pid, stateDb, minLogSeconds) {
|
|
162
|
+
return [
|
|
163
|
+
`ATTACH DATABASE ${sqlString(stateDb)} AS state;`,
|
|
164
|
+
'SELECT id || char(9) || rollout_path || char(9) || last_ts FROM (',
|
|
165
|
+
' SELECT t.id AS id, t.rollout_path AS rollout_path, (',
|
|
166
|
+
' SELECT MAX(l.ts) FROM logs l',
|
|
167
|
+
' WHERE l.thread_id = t.id',
|
|
168
|
+
` AND l.ts >= ${minLogSeconds}`,
|
|
169
|
+
` AND l.process_uuid LIKE ${sqlString(`pid:${pid}:%`)}`,
|
|
170
|
+
' ) AS last_ts',
|
|
171
|
+
' FROM state.threads t',
|
|
172
|
+
" WHERE t.source = 'cli'",
|
|
173
|
+
" AND t.model_provider = 'openai'",
|
|
174
|
+
' AND t.rollout_path IS NOT NULL',
|
|
175
|
+
')',
|
|
176
|
+
'WHERE last_ts IS NOT NULL',
|
|
177
|
+
'ORDER BY last_ts DESC',
|
|
178
|
+
'LIMIT 50;'
|
|
179
|
+
].join('\n');
|
|
194
180
|
}
|
|
195
181
|
function isCodexRolloutPath(filePath, sessionId) {
|
|
196
182
|
const base = path.basename(filePath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codexTranscriptSource.js","sourceRoot":"","sources":["../../../src/server/terminal/codexTranscriptSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"codexTranscriptSource.js","sourceRoot":"","sources":["../../../src/server/terminal/codexTranscriptSource.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
export declare const defaultOutputBufferMaxSerializedBytes: number;
|
|
1
2
|
export declare class OutputBuffer {
|
|
2
3
|
private readonly maxChunks;
|
|
4
|
+
private readonly maxSerializedBytes;
|
|
3
5
|
private readonly chunks;
|
|
4
|
-
|
|
6
|
+
private readonly serializedBytesByChunk;
|
|
7
|
+
private serializedBytes;
|
|
8
|
+
constructor(maxChunks?: number, maxSerializedBytes?: number);
|
|
5
9
|
push(chunk: string): void;
|
|
6
10
|
snapshot(): string[];
|
|
7
11
|
}
|
|
@@ -1,17 +1,43 @@
|
|
|
1
|
+
export const defaultOutputBufferMaxSerializedBytes = 4 * 1024 * 1024;
|
|
1
2
|
export class OutputBuffer {
|
|
2
3
|
maxChunks;
|
|
4
|
+
maxSerializedBytes;
|
|
3
5
|
chunks = [];
|
|
4
|
-
|
|
6
|
+
serializedBytesByChunk = [];
|
|
7
|
+
serializedBytes = 0;
|
|
8
|
+
constructor(maxChunks = 500, maxSerializedBytes = defaultOutputBufferMaxSerializedBytes) {
|
|
5
9
|
this.maxChunks = maxChunks;
|
|
10
|
+
this.maxSerializedBytes = maxSerializedBytes;
|
|
6
11
|
}
|
|
7
12
|
push(chunk) {
|
|
8
|
-
this.
|
|
9
|
-
|
|
13
|
+
const boundedChunk = boundChunkForJson(chunk, this.maxSerializedBytes);
|
|
14
|
+
// Include one byte of array delimiter overhead per retained chunk. This
|
|
15
|
+
// is conservative for the first item and keeps the final JSON array under
|
|
16
|
+
// the same practical budget.
|
|
17
|
+
const bytes = jsonStringBytes(boundedChunk) + 1;
|
|
18
|
+
this.chunks.push(boundedChunk);
|
|
19
|
+
this.serializedBytesByChunk.push(bytes);
|
|
20
|
+
this.serializedBytes += bytes;
|
|
21
|
+
while (this.chunks.length > this.maxChunks || this.serializedBytes > this.maxSerializedBytes) {
|
|
10
22
|
this.chunks.shift();
|
|
23
|
+
this.serializedBytes -= this.serializedBytesByChunk.shift() ?? 0;
|
|
11
24
|
}
|
|
12
25
|
}
|
|
13
26
|
snapshot() {
|
|
14
27
|
return [...this.chunks];
|
|
15
28
|
}
|
|
16
29
|
}
|
|
30
|
+
function boundChunkForJson(chunk, maxSerializedBytes) {
|
|
31
|
+
if (jsonStringBytes(chunk) <= maxSerializedBytes) {
|
|
32
|
+
return chunk;
|
|
33
|
+
}
|
|
34
|
+
// A JSON string can use at most six bytes per UTF-16 code unit (`\uXXXX`).
|
|
35
|
+
// Keeping this many trailing units therefore fits without repeatedly
|
|
36
|
+
// stringifying a potentially huge PTY chunk.
|
|
37
|
+
const maxCodeUnits = Math.max(0, Math.floor((maxSerializedBytes - 2) / 6));
|
|
38
|
+
return maxCodeUnits === 0 ? '' : chunk.slice(-maxCodeUnits);
|
|
39
|
+
}
|
|
40
|
+
function jsonStringBytes(value) {
|
|
41
|
+
return Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
42
|
+
}
|
|
17
43
|
//# sourceMappingURL=outputBuffer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outputBuffer.js","sourceRoot":"","sources":["../../../src/server/terminal/outputBuffer.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,YAAY;
|
|
1
|
+
{"version":3,"file":"outputBuffer.js","sourceRoot":"","sources":["../../../src/server/terminal/outputBuffer.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAErE,MAAM,OAAO,YAAY;IAMJ;IACA;IANF,MAAM,GAAa,EAAE,CAAC;IACtB,sBAAsB,GAAa,EAAE,CAAC;IAC/C,eAAe,GAAG,CAAC,CAAC;IAE5B,YACmB,YAAY,GAAG,EACf,qBAAqB,qCAAqC;QAD1D,cAAS,GAAT,SAAS,CAAM;QACf,uBAAkB,GAAlB,kBAAkB,CAAwC;IAC1E,CAAC;IAEJ,IAAI,CAAC,KAAa;QAChB,MAAM,YAAY,GAAG,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACvE,wEAAwE;QACxE,0EAA0E;QAC1E,6BAA6B;QAC7B,MAAM,KAAK,GAAG,eAAe,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/B,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC;QAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7F,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACpB,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,QAAQ;QACN,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,SAAS,iBAAiB,CAAC,KAAa,EAAE,kBAA0B;IAClE,IAAI,eAAe,CAAC,KAAK,CAAC,IAAI,kBAAkB,EAAE,CAAC;QACjD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,2EAA2E;IAC3E,qEAAqE;IACrE,6CAA6C;IAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,kBAAkB,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC3E,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export declare function
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function readProcessEnvValues(pid: number, keys: readonly string[]): Map<string, string>;
|
|
1
|
+
export declare function readProcessStartTicksAsync(pid: number): Promise<string | null>;
|
|
2
|
+
export declare function processStartTicksToSecondsAsync(startTicks: string | null): Promise<number | null>;
|
|
3
|
+
export declare function readProcessStartedAtSecondsAsync(pid: number): Promise<number | null>;
|
|
4
|
+
export declare function readBootTimeSecondsAsync(): Promise<number | null>;
|
|
5
|
+
export declare function readClockTicksPerSecondAsync(): Promise<number | null>;
|
|
6
|
+
export declare function readProcessCwdAsync(pid: number): Promise<string | null>;
|
|
7
|
+
export declare function readProcessEnvValuesAsync(pid: number, keys: readonly string[]): Promise<Map<string, string>>;
|
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { readFile, readlink } from 'node:fs/promises';
|
|
2
|
+
import { KeyedSingleFlight, runCommand } from './asyncCommand.js';
|
|
3
3
|
// Shared /proc readers for the transcript resolvers. The stat field-22 parse
|
|
4
4
|
// is subtle (comm may contain spaces and parens, hence the lastIndexOf(')')
|
|
5
5
|
// anchor), so it lives in exactly one place: both Claude registry pid-reuse
|
|
6
6
|
// detection and Codex process-log time bounds depend on the same definition.
|
|
7
7
|
let cachedClockTicksPerSecond = null;
|
|
8
|
+
let cachedBootTimeSeconds = null;
|
|
9
|
+
const clockTicksLookup = new KeyedSingleFlight();
|
|
10
|
+
const bootTimeLookup = new KeyedSingleFlight();
|
|
8
11
|
// /proc/<pid>/stat field 22 (starttime) as the raw tick string. Returned
|
|
9
12
|
// unconverted because Claude's session registry stores the same value
|
|
10
13
|
// verbatim — string equality is the most faithful comparison.
|
|
11
|
-
export function
|
|
12
|
-
let raw;
|
|
14
|
+
export async function readProcessStartTicksAsync(pid) {
|
|
13
15
|
try {
|
|
14
|
-
|
|
16
|
+
return parseProcessStartTicks(await readFile(`/proc/${pid}/stat`, 'utf8'));
|
|
15
17
|
}
|
|
16
18
|
catch {
|
|
17
19
|
return null;
|
|
18
20
|
}
|
|
21
|
+
}
|
|
22
|
+
function parseProcessStartTicks(raw) {
|
|
19
23
|
const closeParen = raw.lastIndexOf(')');
|
|
20
24
|
if (closeParen === -1) {
|
|
21
25
|
return null;
|
|
@@ -27,32 +31,35 @@ export function readProcessStartTicks(pid) {
|
|
|
27
31
|
// null stays null: a failed stat read must not degrade into "started at
|
|
28
32
|
// boot" (Number(null) === 0), which would silently change downstream
|
|
29
33
|
// mtime-plausibility filtering.
|
|
30
|
-
export function
|
|
34
|
+
export async function processStartTicksToSecondsAsync(startTicks) {
|
|
31
35
|
if (startTicks === null) {
|
|
32
36
|
return null;
|
|
33
37
|
}
|
|
34
|
-
const bootTime =
|
|
35
|
-
const clockTicks = readClockTicksPerSecond();
|
|
38
|
+
const [bootTime, clockTicks] = await Promise.all([readBootTimeSecondsAsync(), readClockTicksPerSecondAsync()]);
|
|
36
39
|
if (bootTime === null || clockTicks === null) {
|
|
37
40
|
return null;
|
|
38
41
|
}
|
|
39
42
|
return bootTime + Number(startTicks) / clockTicks;
|
|
40
43
|
}
|
|
41
|
-
export function
|
|
42
|
-
return
|
|
44
|
+
export async function readProcessStartedAtSecondsAsync(pid) {
|
|
45
|
+
return processStartTicksToSecondsAsync(await readProcessStartTicksAsync(pid));
|
|
43
46
|
}
|
|
44
|
-
export function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
export async function readBootTimeSecondsAsync() {
|
|
48
|
+
if (cachedBootTimeSeconds !== null)
|
|
49
|
+
return cachedBootTimeSeconds;
|
|
50
|
+
return bootTimeLookup.run('boot-time', async () => {
|
|
51
|
+
try {
|
|
52
|
+
const parsed = parseBootTimeSeconds(await readFile('/proc/stat', 'utf8'));
|
|
53
|
+
if (parsed !== null)
|
|
54
|
+
cachedBootTimeSeconds = parsed;
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
47
61
|
}
|
|
48
|
-
|
|
49
|
-
let raw;
|
|
50
|
-
try {
|
|
51
|
-
raw = readFileSync('/proc/stat', 'utf8');
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
62
|
+
function parseBootTimeSeconds(raw) {
|
|
56
63
|
const line = raw.split('\n').find((entry) => entry.startsWith('btime '));
|
|
57
64
|
if (!line) {
|
|
58
65
|
return null;
|
|
@@ -60,25 +67,28 @@ export function readBootTimeSeconds() {
|
|
|
60
67
|
const bootTime = Number(line.slice('btime '.length).trim());
|
|
61
68
|
return Number.isFinite(bootTime) && bootTime > 0 ? bootTime : null;
|
|
62
69
|
}
|
|
63
|
-
export function
|
|
70
|
+
export async function readClockTicksPerSecondAsync() {
|
|
64
71
|
if (cachedClockTicksPerSecond !== null) {
|
|
65
72
|
return cachedClockTicksPerSecond;
|
|
66
73
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
return clockTicksLookup.run('clock-ticks', async () => {
|
|
75
|
+
try {
|
|
76
|
+
const result = await runCommand('getconf', ['CLK_TCK'], { timeoutMs: 1_000, maxBuffer: 1024 });
|
|
77
|
+
const ticks = Number(result.stdout.trim());
|
|
78
|
+
if (!Number.isFinite(ticks) || ticks <= 0) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
cachedClockTicksPerSecond = ticks;
|
|
82
|
+
return ticks;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
71
87
|
});
|
|
72
|
-
const ticks = Number(result.stdout?.trim());
|
|
73
|
-
if (!Number.isFinite(ticks) || ticks <= 0) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
cachedClockTicksPerSecond = ticks;
|
|
77
|
-
return ticks;
|
|
78
88
|
}
|
|
79
|
-
export function
|
|
89
|
+
export async function readProcessCwdAsync(pid) {
|
|
80
90
|
try {
|
|
81
|
-
return
|
|
91
|
+
return await readlink(`/proc/${pid}/cwd`);
|
|
82
92
|
}
|
|
83
93
|
catch {
|
|
84
94
|
return null;
|
|
@@ -86,27 +96,26 @@ export function readProcessCwd(pid) {
|
|
|
86
96
|
}
|
|
87
97
|
// One environ read serving several keys — the buffer is commonly multiple KB
|
|
88
98
|
// and per-key callers used to re-read and re-split it for every variable.
|
|
89
|
-
export function
|
|
90
|
-
const out = new Map();
|
|
91
|
-
let raw;
|
|
99
|
+
export async function readProcessEnvValuesAsync(pid, keys) {
|
|
92
100
|
try {
|
|
93
|
-
|
|
101
|
+
return parseProcessEnvValues(await readFile(`/proc/${pid}/environ`, 'utf8'), keys);
|
|
94
102
|
}
|
|
95
103
|
catch {
|
|
96
|
-
return
|
|
104
|
+
return new Map();
|
|
97
105
|
}
|
|
106
|
+
}
|
|
107
|
+
function parseProcessEnvValues(raw, keys) {
|
|
108
|
+
const out = new Map();
|
|
98
109
|
for (const entry of raw.split('\0')) {
|
|
99
110
|
const separator = entry.indexOf('=');
|
|
100
|
-
if (separator === -1)
|
|
111
|
+
if (separator === -1)
|
|
101
112
|
continue;
|
|
102
|
-
}
|
|
103
113
|
const key = entry.slice(0, separator);
|
|
104
|
-
if (keys.includes(key)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
114
|
+
if (!keys.includes(key) || out.has(key))
|
|
115
|
+
continue;
|
|
116
|
+
const value = entry.slice(separator + 1);
|
|
117
|
+
if (value)
|
|
118
|
+
out.set(key, value);
|
|
110
119
|
}
|
|
111
120
|
return out;
|
|
112
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"procfs.js","sourceRoot":"","sources":["../../../src/server/terminal/procfs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"procfs.js","sourceRoot":"","sources":["../../../src/server/terminal/procfs.ts"],"names":[],"mappings":"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"}
|