nterminal 1.2.56 → 1.2.58
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/dist/client/assets/MarkdownPreview-D-oOD4FK.js +1 -0
- package/dist/client/assets/{TranscriptMarkdownBody-CDKYjgIZ.js → TranscriptMarkdownBody-BDE5VmlN.js} +1 -1
- package/dist/client/assets/index-1p5j5ZUZ.js +54 -0
- package/dist/client/assets/{index-fsgQPmdV.js → index-DLVKSDlo.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/server/auth/authService.d.ts +6 -3
- package/dist/server/auth/authService.js +80 -28
- package/dist/server/auth/authService.js.map +1 -1
- package/dist/server/storage/fileStore.d.ts +7 -0
- package/dist/server/storage/fileStore.js +10 -0
- package/dist/server/storage/fileStore.js.map +1 -1
- package/dist/server/terminal/TmuxPtyAdapter.d.ts +28 -2
- package/dist/server/terminal/TmuxPtyAdapter.js +295 -122
- package/dist/server/terminal/TmuxPtyAdapter.js.map +1 -1
- package/dist/server/terminal/codexTranscriptSource.d.ts +5 -0
- package/dist/server/terminal/codexTranscriptSource.js +140 -82
- package/dist/server/terminal/codexTranscriptSource.js.map +1 -1
- package/dist/server/terminal/procfs.d.ts +8 -0
- package/dist/server/terminal/procfs.js +113 -0
- package/dist/server/terminal/procfs.js.map +1 -0
- package/dist/server/terminal/sessionId.d.ts +3 -0
- package/dist/server/terminal/sessionId.js +10 -0
- package/dist/server/terminal/sessionId.js.map +1 -0
- package/dist/server/terminal/ttlCache.d.ts +8 -0
- package/dist/server/terminal/ttlCache.js +33 -0
- package/dist/server/terminal/ttlCache.js.map +1 -0
- package/package.json +1 -1
- package/dist/client/assets/MarkdownPreview-Bk02CJkU.js +0 -1
- package/dist/client/assets/index-BBVLYVi6.js +0 -54
|
@@ -1,9 +1,92 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, readlinkSync } from 'node:fs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { isSessionId, sessionIdPatternSource } from './sessionId.js';
|
|
6
|
+
import { readProcessStartedAtSeconds } from './procfs.js';
|
|
7
|
+
import { TtlCache } from './ttlCache.js';
|
|
5
8
|
const PROCESS_START_SKEW_SECONDS = 2;
|
|
6
|
-
|
|
9
|
+
const LOOKUP_CACHE_SUCCESS_TTL_MS = 30_000;
|
|
10
|
+
// Must exceed the client's steady transcript-source poll cadence (15s in
|
|
11
|
+
// XtermPane); a shorter negative TTL guarantees every poll misses the cache
|
|
12
|
+
// and re-runs the event-loop-blocking sqlite3 shell-out.
|
|
13
|
+
const LOOKUP_CACHE_FAILURE_TTL_MS = 20_000;
|
|
14
|
+
// The optional middle segment keeps this in sync with the loose
|
|
15
|
+
// startsWith('rollout-')/endsWith('<id>.jsonl') checks used elsewhere — a
|
|
16
|
+
// rollout named without the timestamp segment must not silently bypass the
|
|
17
|
+
// fd scan while passing the other matchers.
|
|
18
|
+
const CODEX_ROLLOUT_FILE_PATTERN = new RegExp(`^rollout-(?:.*-)?(${sessionIdPatternSource})\\.jsonl$`);
|
|
19
|
+
const processLogLookupCache = new TtlCache();
|
|
20
|
+
export function clearCodexTranscriptLookupCache() {
|
|
21
|
+
processLogLookupCache.clear();
|
|
22
|
+
}
|
|
23
|
+
// The cheapest and most current mapping there is: the codex binary keeps its
|
|
24
|
+
// active rollout JSONL open O_WRONLY|O_APPEND for the whole session, so
|
|
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) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const procRoot = options.procRoot ?? '/proc';
|
|
36
|
+
const fdDir = path.join(procRoot, String(pid), 'fd');
|
|
37
|
+
let fdNames;
|
|
38
|
+
try {
|
|
39
|
+
fdNames = readdirSync(fdDir);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const openRollouts = new Map();
|
|
45
|
+
for (const fdName of fdNames) {
|
|
46
|
+
let target;
|
|
47
|
+
try {
|
|
48
|
+
target = readlinkSync(path.join(fdDir, fdName));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (target.endsWith(' (deleted)')) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const match = CODEX_ROLLOUT_FILE_PATTERN.exec(path.basename(target));
|
|
57
|
+
if (!match || !isFdOpenForWriting(procRoot, pid, fdName)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
openRollouts.set(match[1], target);
|
|
61
|
+
}
|
|
62
|
+
if (openRollouts.size !== 1) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const [sessionId, filePath] = [...openRollouts.entries()][0];
|
|
66
|
+
if (!existsSync(filePath)) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return { provider: 'codex', sessionId, filePath };
|
|
70
|
+
}
|
|
71
|
+
function isFdOpenForWriting(procRoot, pid, fdName) {
|
|
72
|
+
let raw;
|
|
73
|
+
try {
|
|
74
|
+
raw = readFileSync(path.join(procRoot, String(pid), 'fdinfo', fdName), 'utf8');
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const line = raw.split('\n').find((candidate) => candidate.startsWith('flags:'));
|
|
80
|
+
if (!line) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const flags = parseInt(line.slice('flags:'.length).trim(), 8);
|
|
84
|
+
if (!Number.isFinite(flags)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const accessMode = flags & 0o3;
|
|
88
|
+
return accessMode === 0o1 || accessMode === 0o2;
|
|
89
|
+
}
|
|
7
90
|
export function resolveCodexTranscriptFromProcessLogs(pid, options = {}) {
|
|
8
91
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
9
92
|
return null;
|
|
@@ -21,19 +104,48 @@ export function resolveCodexTranscriptFromProcessLogs(pid, options = {}) {
|
|
|
21
104
|
if (processStartedAtSeconds === null) {
|
|
22
105
|
return null;
|
|
23
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
|
+
const cacheKey = `${logsDb}:${pid}:${processStartedAtSeconds}`;
|
|
112
|
+
const cached = processLogLookupCache.get(cacheKey);
|
|
113
|
+
if (cached) {
|
|
114
|
+
return cached.value;
|
|
115
|
+
}
|
|
116
|
+
const source = lookupCodexTranscriptInProcessLogs(pid, logsDb, stateDb, processStartedAtSeconds, options.runSqlite);
|
|
117
|
+
processLogLookupCache.set(cacheKey, source, source ? LOOKUP_CACHE_SUCCESS_TTL_MS : LOOKUP_CACHE_FAILURE_TTL_MS);
|
|
118
|
+
return source;
|
|
119
|
+
}
|
|
120
|
+
function lookupCodexTranscriptInProcessLogs(pid, logsDb, stateDb, processStartedAtSeconds, runSqliteOverride) {
|
|
24
121
|
const minLogSeconds = Math.max(0, Math.floor(processStartedAtSeconds) - PROCESS_START_SKEW_SECONDS);
|
|
25
|
-
|
|
122
|
+
// Driven from the small threads table with an indexed probe into logs per
|
|
123
|
+
// thread. The previous shape walked the ts index across every process's
|
|
124
|
+
// rows from the cutoff onward, evaluating process_uuid LIKE row by row
|
|
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, [
|
|
26
134
|
`ATTACH DATABASE ${sqlString(stateDb)} AS state;`,
|
|
27
|
-
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`AND l.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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',
|
|
37
149
|
'LIMIT 50;'
|
|
38
150
|
].join('\n'));
|
|
39
151
|
if (output === null) {
|
|
@@ -44,25 +156,30 @@ export function resolveCodexTranscriptFromProcessLogs(pid, options = {}) {
|
|
|
44
156
|
const trimmed = line.trim();
|
|
45
157
|
if (!trimmed)
|
|
46
158
|
continue;
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
159
|
+
const [sessionId, filePath, lastTsRaw] = trimmed.split('\t');
|
|
160
|
+
if (!sessionId || !filePath)
|
|
49
161
|
continue;
|
|
50
|
-
const sessionId = trimmed.slice(0, separator);
|
|
51
|
-
const filePath = trimmed.slice(separator + 1);
|
|
52
162
|
if (!isSessionId(sessionId) || !isCodexRolloutPath(filePath, sessionId) || !existsSync(filePath)) {
|
|
53
163
|
continue;
|
|
54
164
|
}
|
|
55
|
-
|
|
165
|
+
const lastLogSeconds = Number(lastTsRaw);
|
|
166
|
+
const normalized = Number.isFinite(lastLogSeconds) ? lastLogSeconds : 0;
|
|
167
|
+
const existing = matches.get(sessionId);
|
|
168
|
+
if (!existing || existing.lastLogSeconds < normalized) {
|
|
169
|
+
matches.set(sessionId, { filePath, lastLogSeconds: normalized });
|
|
170
|
+
}
|
|
56
171
|
}
|
|
57
|
-
if (matches.size
|
|
172
|
+
if (matches.size === 0) {
|
|
58
173
|
return null;
|
|
59
174
|
}
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
175
|
+
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.
|
|
62
179
|
return null;
|
|
63
180
|
}
|
|
64
|
-
const [sessionId,
|
|
65
|
-
return { provider: 'codex', sessionId, filePath };
|
|
181
|
+
const [sessionId, entry] = ranked[0];
|
|
182
|
+
return { provider: 'codex', sessionId, filePath: entry.filePath };
|
|
66
183
|
}
|
|
67
184
|
function runSqlite(databasePath, sql) {
|
|
68
185
|
const result = spawnSync('sqlite3', ['-batch', '-noheader', databasePath, sql], {
|
|
@@ -75,69 +192,10 @@ function runSqlite(databasePath, sql) {
|
|
|
75
192
|
}
|
|
76
193
|
return result.stdout;
|
|
77
194
|
}
|
|
78
|
-
function readProcessStartedAtSeconds(pid) {
|
|
79
|
-
const bootTime = readBootTimeSeconds();
|
|
80
|
-
const clockTicks = readClockTicksPerSecond();
|
|
81
|
-
if (bootTime === null || clockTicks === null) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
let raw;
|
|
85
|
-
try {
|
|
86
|
-
raw = readFileSync(`/proc/${pid}/stat`, 'utf8');
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
const closeParen = raw.lastIndexOf(')');
|
|
92
|
-
if (closeParen === -1) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
const fieldsAfterCommand = raw.slice(closeParen + 2).trim().split(/\s+/);
|
|
96
|
-
const startTicksRaw = fieldsAfterCommand[19];
|
|
97
|
-
const startTicks = Number(startTicksRaw);
|
|
98
|
-
if (!Number.isFinite(startTicks) || startTicks < 0) {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
return bootTime + startTicks / clockTicks;
|
|
102
|
-
}
|
|
103
|
-
function readBootTimeSeconds() {
|
|
104
|
-
let raw;
|
|
105
|
-
try {
|
|
106
|
-
raw = readFileSync('/proc/stat', 'utf8');
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
const line = raw.split('\n').find((entry) => entry.startsWith('btime '));
|
|
112
|
-
if (!line) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
const bootTime = Number(line.slice('btime '.length).trim());
|
|
116
|
-
return Number.isFinite(bootTime) && bootTime > 0 ? bootTime : null;
|
|
117
|
-
}
|
|
118
|
-
function readClockTicksPerSecond() {
|
|
119
|
-
if (cachedClockTicksPerSecond !== null) {
|
|
120
|
-
return cachedClockTicksPerSecond;
|
|
121
|
-
}
|
|
122
|
-
const result = spawnSync('getconf', ['CLK_TCK'], {
|
|
123
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
124
|
-
encoding: 'utf8',
|
|
125
|
-
timeout: 1000
|
|
126
|
-
});
|
|
127
|
-
const ticks = Number(result.stdout.trim());
|
|
128
|
-
if (!Number.isFinite(ticks) || ticks <= 0) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
cachedClockTicksPerSecond = ticks;
|
|
132
|
-
return ticks;
|
|
133
|
-
}
|
|
134
195
|
function isCodexRolloutPath(filePath, sessionId) {
|
|
135
196
|
const base = path.basename(filePath);
|
|
136
197
|
return base.startsWith('rollout-') && base.endsWith(`${sessionId}.jsonl`);
|
|
137
198
|
}
|
|
138
|
-
function isSessionId(value) {
|
|
139
|
-
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(value);
|
|
140
|
-
}
|
|
141
199
|
function sqlString(value) {
|
|
142
200
|
return `'${value.replace(/'/g, "''")}'`;
|
|
143
201
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codexTranscriptSource.js","sourceRoot":"","sources":["../../../src/server/terminal/codexTranscriptSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"codexTranscriptSource.js","sourceRoot":"","sources":["../../../src/server/terminal/codexTranscriptSource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function readProcessStartTicks(pid: number): string | null;
|
|
2
|
+
export declare function processStartTicksToSeconds(startTicks: string | null): number | null;
|
|
3
|
+
export declare function readProcessStartedAtSeconds(pid: number): number | null;
|
|
4
|
+
export declare function readProcessStartedAtMilliseconds(pid: number): number | null;
|
|
5
|
+
export declare function readBootTimeSeconds(): number | null;
|
|
6
|
+
export declare function readClockTicksPerSecond(): number | null;
|
|
7
|
+
export declare function readProcessCwd(pid: number): string | null;
|
|
8
|
+
export declare function readProcessEnvValues(pid: number, keys: readonly string[]): Map<string, string>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync, readlinkSync } from 'node:fs';
|
|
3
|
+
// Shared /proc readers for the transcript resolvers. The stat field-22 parse
|
|
4
|
+
// is subtle (comm may contain spaces and parens, hence the lastIndexOf(')')
|
|
5
|
+
// anchor), so it lives in exactly one place: both Claude registry pid-reuse
|
|
6
|
+
// detection and Codex process-log time bounds depend on the same definition.
|
|
7
|
+
let cachedClockTicksPerSecond = null;
|
|
8
|
+
// /proc/<pid>/stat field 22 (starttime) as the raw tick string. Returned
|
|
9
|
+
// unconverted because Claude's session registry stores the same value
|
|
10
|
+
// verbatim — string equality is the most faithful comparison.
|
|
11
|
+
export function readProcessStartTicks(pid) {
|
|
12
|
+
let raw;
|
|
13
|
+
try {
|
|
14
|
+
raw = readFileSync(`/proc/${pid}/stat`, 'utf8');
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const closeParen = raw.lastIndexOf(')');
|
|
20
|
+
if (closeParen === -1) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const fieldsAfterCommand = raw.slice(closeParen + 2).trim().split(/\s+/);
|
|
24
|
+
const startTicks = fieldsAfterCommand[19];
|
|
25
|
+
return startTicks && /^\d+$/.test(startTicks) ? startTicks : null;
|
|
26
|
+
}
|
|
27
|
+
// null stays null: a failed stat read must not degrade into "started at
|
|
28
|
+
// boot" (Number(null) === 0), which would silently change downstream
|
|
29
|
+
// mtime-plausibility filtering.
|
|
30
|
+
export function processStartTicksToSeconds(startTicks) {
|
|
31
|
+
if (startTicks === null) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const bootTime = readBootTimeSeconds();
|
|
35
|
+
const clockTicks = readClockTicksPerSecond();
|
|
36
|
+
if (bootTime === null || clockTicks === null) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return bootTime + Number(startTicks) / clockTicks;
|
|
40
|
+
}
|
|
41
|
+
export function readProcessStartedAtSeconds(pid) {
|
|
42
|
+
return processStartTicksToSeconds(readProcessStartTicks(pid));
|
|
43
|
+
}
|
|
44
|
+
export function readProcessStartedAtMilliseconds(pid) {
|
|
45
|
+
const seconds = readProcessStartedAtSeconds(pid);
|
|
46
|
+
return seconds === null ? null : seconds * 1000;
|
|
47
|
+
}
|
|
48
|
+
export function readBootTimeSeconds() {
|
|
49
|
+
let raw;
|
|
50
|
+
try {
|
|
51
|
+
raw = readFileSync('/proc/stat', 'utf8');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const line = raw.split('\n').find((entry) => entry.startsWith('btime '));
|
|
57
|
+
if (!line) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const bootTime = Number(line.slice('btime '.length).trim());
|
|
61
|
+
return Number.isFinite(bootTime) && bootTime > 0 ? bootTime : null;
|
|
62
|
+
}
|
|
63
|
+
export function readClockTicksPerSecond() {
|
|
64
|
+
if (cachedClockTicksPerSecond !== null) {
|
|
65
|
+
return cachedClockTicksPerSecond;
|
|
66
|
+
}
|
|
67
|
+
const result = spawnSync('getconf', ['CLK_TCK'], {
|
|
68
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
69
|
+
encoding: 'utf8',
|
|
70
|
+
timeout: 1000
|
|
71
|
+
});
|
|
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
|
+
}
|
|
79
|
+
export function readProcessCwd(pid) {
|
|
80
|
+
try {
|
|
81
|
+
return readlinkSync(`/proc/${pid}/cwd`);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// One environ read serving several keys — the buffer is commonly multiple KB
|
|
88
|
+
// and per-key callers used to re-read and re-split it for every variable.
|
|
89
|
+
export function readProcessEnvValues(pid, keys) {
|
|
90
|
+
const out = new Map();
|
|
91
|
+
let raw;
|
|
92
|
+
try {
|
|
93
|
+
raw = readFileSync(`/proc/${pid}/environ`, 'utf8');
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of raw.split('\0')) {
|
|
99
|
+
const separator = entry.indexOf('=');
|
|
100
|
+
if (separator === -1) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const key = entry.slice(0, separator);
|
|
104
|
+
if (keys.includes(key) && !out.has(key)) {
|
|
105
|
+
const value = entry.slice(separator + 1);
|
|
106
|
+
if (value) {
|
|
107
|
+
out.set(key, value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=procfs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"procfs.js","sourceRoot":"","sources":["../../../src/server/terminal/procfs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Single source of truth for the UUID shape Codex thread ids and Claude
|
|
2
|
+
// session ids share. Both resolvers and the rollout-filename pattern build on
|
|
3
|
+
// this so the definition can never drift between code paths (a drift would
|
|
4
|
+
// make the same pane resolve through one mechanism and fail through another).
|
|
5
|
+
export const sessionIdPatternSource = '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
|
|
6
|
+
export const SESSION_ID_PATTERN = new RegExp(`^${sessionIdPatternSource}$`);
|
|
7
|
+
export function isSessionId(value) {
|
|
8
|
+
return typeof value === 'string' && SESSION_ID_PATTERN.test(value);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=sessionId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionId.js","sourceRoot":"","sources":["../../../src/server/terminal/sessionId.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAC9E,MAAM,CAAC,MAAM,sBAAsB,GACjC,6EAA6E,CAAC;AAEhF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,MAAM,CAAC,IAAI,sBAAsB,GAAG,CAAC,CAAC;AAE5E,MAAM,UAAU,WAAW,CAAC,KAAyB;IACnD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Tiny TTL map used by the transcript resolvers. Unlike a bare Map with
|
|
2
|
+
// expiresAt fields, expired entries are actually removed — on read of the
|
|
3
|
+
// expired key and by a sweep on every write — so long-lived servers don't
|
|
4
|
+
// accumulate one dead entry per Claude /clear or per Codex process forever.
|
|
5
|
+
// `get` returns a wrapper object so cached `null` values (negative results)
|
|
6
|
+
// are distinguishable from a cache miss.
|
|
7
|
+
export class TtlCache {
|
|
8
|
+
entries = new Map();
|
|
9
|
+
get(key) {
|
|
10
|
+
const entry = this.entries.get(key);
|
|
11
|
+
if (!entry) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (entry.expiresAt <= Date.now()) {
|
|
15
|
+
this.entries.delete(key);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return { value: entry.value };
|
|
19
|
+
}
|
|
20
|
+
set(key, value, ttlMs) {
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
for (const [existingKey, entry] of this.entries) {
|
|
23
|
+
if (entry.expiresAt <= now) {
|
|
24
|
+
this.entries.delete(existingKey);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
this.entries.set(key, { expiresAt: now + ttlMs, value });
|
|
28
|
+
}
|
|
29
|
+
clear() {
|
|
30
|
+
this.entries.clear();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=ttlCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ttlCache.js","sourceRoot":"","sources":["../../../src/server/terminal/ttlCache.ts"],"names":[],"mappings":"AAKA,wEAAwE;AACxE,0EAA0E;AAC1E,0EAA0E;AAC1E,4EAA4E;AAC5E,4EAA4E;AAC5E,yCAAyC;AACzC,MAAM,OAAO,QAAQ;IACF,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IAE/D,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAChC,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAQ,EAAE,KAAa;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChD,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,CAAC;gBAC3B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as a}from"./index-BBVLYVi6.js";import{M as e,r as o}from"./index-fsgQPmdV.js";function s({content:r}){return a.jsx(e,{remarkPlugins:[o],children:r})}export{s as default};
|