@respan/cli 0.17.1 → 0.17.3

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.
@@ -23,15 +23,37 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
25
  // src/hooks/claude-code.ts
26
+ var fs3 = __toESM(require("node:fs"), 1);
27
+ var os3 = __toESM(require("node:os"), 1);
28
+ var path3 = __toESM(require("node:path"), 1);
29
+ var import_node_child_process = require("node:child_process");
30
+
31
+ // src/hooks/shared.ts
26
32
  var fs2 = __toESM(require("node:fs"), 1);
27
33
  var os2 = __toESM(require("node:os"), 1);
28
34
  var path2 = __toESM(require("node:path"), 1);
29
- var import_node_child_process = require("node:child_process");
30
35
 
31
- // src/hooks/shared.ts
36
+ // src/lib/config.ts
32
37
  var fs = __toESM(require("node:fs"), 1);
33
- var os = __toESM(require("node:os"), 1);
34
38
  var path = __toESM(require("node:path"), 1);
39
+ var os = __toESM(require("node:os"), 1);
40
+ var CONFIG_DIR = path.join(os.homedir(), ".respan");
41
+ var CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
42
+ var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
43
+ function restrictToOwner(target) {
44
+ if (process.platform === "win32") return;
45
+ try {
46
+ const mode = fs.statSync(target).mode & 511;
47
+ if (mode & 63) fs.chmodSync(target, mode & 448);
48
+ } catch {
49
+ }
50
+ }
51
+ function tightenCredentialPermissions() {
52
+ restrictToOwner(CONFIG_DIR);
53
+ restrictToOwner(CREDENTIALS_FILE);
54
+ }
55
+
56
+ // src/hooks/shared.ts
35
57
  function resolveTracesEndpoint(baseUrl) {
36
58
  const DEFAULT = "https://api.respan.ai/api/v2/traces";
37
59
  if (!baseUrl) return DEFAULT;
@@ -44,13 +66,13 @@ var _debug = false;
44
66
  function initLogging(logFile, debug2) {
45
67
  _logFile = logFile;
46
68
  _debug = debug2;
47
- const dir = path.dirname(logFile);
48
- fs.mkdirSync(dir, { recursive: true });
69
+ const dir = path2.dirname(logFile);
70
+ fs2.mkdirSync(dir, { recursive: true });
49
71
  }
50
72
  function log(level, message) {
51
73
  if (!_logFile) return;
52
74
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
53
- fs.appendFileSync(_logFile, `${ts} [${level}] ${message}
75
+ fs2.appendFileSync(_logFile, `${ts} [${level}] ${message}
54
76
  `);
55
77
  }
56
78
  function debug(message) {
@@ -61,14 +83,15 @@ function resolveCredentials() {
61
83
  let apiKey = process.env.RESPAN_API_KEY ?? "";
62
84
  let baseUrl = process.env.RESPAN_BASE_URL ?? DEFAULT_BASE_URL;
63
85
  if (!apiKey) {
64
- const credsFile = path.join(os.homedir(), ".respan", "credentials.json");
65
- if (fs.existsSync(credsFile)) {
86
+ const credsFile = path2.join(os2.homedir(), ".respan", "credentials.json");
87
+ if (fs2.existsSync(credsFile)) {
88
+ tightenCredentialPermissions();
66
89
  try {
67
- const creds = JSON.parse(fs.readFileSync(credsFile, "utf-8"));
68
- const configFile = path.join(os.homedir(), ".respan", "config.json");
90
+ const creds = JSON.parse(fs2.readFileSync(credsFile, "utf-8"));
91
+ const configFile = path2.join(os2.homedir(), ".respan", "config.json");
69
92
  let profile = "default";
70
- if (fs.existsSync(configFile)) {
71
- const cfg = JSON.parse(fs.readFileSync(configFile, "utf-8"));
93
+ if (fs2.existsSync(configFile)) {
94
+ const cfg = JSON.parse(fs2.readFileSync(configFile, "utf-8"));
72
95
  profile = cfg.activeProfile ?? "default";
73
96
  }
74
97
  const cred = creds[profile] ?? {};
@@ -98,11 +121,11 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
98
121
  "project_id"
99
122
  ]);
100
123
  function loadRespanConfig(configPath) {
101
- if (!fs.existsSync(configPath)) {
124
+ if (!fs2.existsSync(configPath)) {
102
125
  return { fields: {}, properties: {} };
103
126
  }
104
127
  try {
105
- const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
128
+ const raw = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
106
129
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
107
130
  return { fields: {}, properties: {} };
108
131
  }
@@ -122,36 +145,36 @@ function loadRespanConfig(configPath) {
122
145
  }
123
146
  }
124
147
  function loadState(statePath) {
125
- if (!fs.existsSync(statePath)) return {};
148
+ if (!fs2.existsSync(statePath)) return {};
126
149
  try {
127
- return JSON.parse(fs.readFileSync(statePath, "utf-8"));
150
+ return JSON.parse(fs2.readFileSync(statePath, "utf-8"));
128
151
  } catch {
129
152
  return {};
130
153
  }
131
154
  }
132
155
  function saveState(statePath, state) {
133
- const dir = path.dirname(statePath);
134
- fs.mkdirSync(dir, { recursive: true });
156
+ const dir = path2.dirname(statePath);
157
+ fs2.mkdirSync(dir, { recursive: true });
135
158
  const tmpPath = statePath + ".tmp." + process.pid;
136
159
  try {
137
- fs.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
138
- fs.renameSync(tmpPath, statePath);
160
+ fs2.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
161
+ fs2.renameSync(tmpPath, statePath);
139
162
  } catch (e) {
140
163
  try {
141
- fs.unlinkSync(tmpPath);
164
+ fs2.unlinkSync(tmpPath);
142
165
  } catch {
143
166
  }
144
- fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
167
+ fs2.writeFileSync(statePath, JSON.stringify(state, null, 2));
145
168
  }
146
169
  }
147
170
  function acquireLock(lockPath, timeoutMs = 5e3) {
148
171
  const deadline = Date.now() + timeoutMs;
149
172
  while (Date.now() < deadline) {
150
173
  try {
151
- fs.mkdirSync(lockPath);
174
+ fs2.mkdirSync(lockPath);
152
175
  return () => {
153
176
  try {
154
- fs.rmdirSync(lockPath);
177
+ fs2.rmdirSync(lockPath);
155
178
  } catch {
156
179
  }
157
180
  };
@@ -360,9 +383,9 @@ async function sendSpans(spans, apiKey, baseUrl, context) {
360
383
  const spanNames = spans.map((s) => s.span_name);
361
384
  debug(`Sending ${spans.length} spans (${body.length} bytes) to ${url} for ${context}: ${spanNames.join(", ")}`);
362
385
  if (_debug) {
363
- const debugDir = _logFile ? path.dirname(_logFile) : os.tmpdir();
364
- const debugFile = path.join(debugDir, `respan_spans_${context.replace(/\s+/g, "_")}.json`);
365
- fs.writeFileSync(debugFile, body);
386
+ const debugDir = _logFile ? path2.dirname(_logFile) : os2.tmpdir();
387
+ const debugFile = path2.join(debugDir, `respan_spans_${context.replace(/\s+/g, "_")}.json`);
388
+ fs2.writeFileSync(debugFile, body);
366
389
  debug(`Dumped OTLP payload to ${debugFile}`);
367
390
  }
368
391
  for (let attempt = 0; attempt < 3; attempt++) {
@@ -403,10 +426,10 @@ function sleep(ms) {
403
426
  }
404
427
 
405
428
  // src/hooks/claude-code.ts
406
- var STATE_DIR = path2.join(os2.homedir(), ".claude", "state");
407
- var LOG_FILE = path2.join(STATE_DIR, "respan_hook.log");
408
- var STATE_FILE = path2.join(STATE_DIR, "respan_state.json");
409
- var LOCK_PATH = path2.join(STATE_DIR, "respan_hook.lock");
429
+ var STATE_DIR = path3.join(os3.homedir(), ".claude", "state");
430
+ var LOG_FILE = path3.join(STATE_DIR, "respan_hook.log");
431
+ var STATE_FILE = path3.join(STATE_DIR, "respan_state.json");
432
+ var LOCK_PATH = path3.join(STATE_DIR, "respan_hook.lock");
410
433
  var DEBUG_MODE = (process.env.CC_RESPAN_DEBUG ?? "").toLowerCase() === "true";
411
434
  var MAX_CHARS = parseInt(process.env.CC_RESPAN_MAX_CHARS ?? "4000", 10) || 4e3;
412
435
  initLogging(LOG_FILE, DEBUG_MODE);
@@ -730,7 +753,7 @@ function processTranscript(sessionId, transcriptFile, state, apiKey, baseUrl, co
730
753
  const sessionState = state[sessionId] ?? {};
731
754
  const lastLine = Number(sessionState.last_line ?? 0);
732
755
  const turnCount = Number(sessionState.turn_count ?? 0);
733
- const content = fs2.readFileSync(transcriptFile, "utf-8");
756
+ const content = fs3.readFileSync(transcriptFile, "utf-8");
734
757
  const lines = content.trim().split("\n");
735
758
  const totalLines = lines.length;
736
759
  if (lastLine >= totalLines) {
@@ -831,13 +854,13 @@ function processTranscript(sessionId, transcriptFile, state, apiKey, baseUrl, co
831
854
  function readStdinPayload() {
832
855
  if (process.stdin.isTTY) return null;
833
856
  try {
834
- const raw = fs2.readFileSync(0, "utf-8");
857
+ const raw = fs3.readFileSync(0, "utf-8");
835
858
  if (!raw.trim()) return null;
836
859
  const payload = JSON.parse(raw);
837
860
  const sessionId = String(payload.session_id ?? "");
838
861
  const transcriptPath = String(payload.transcript_path ?? "");
839
862
  if (!sessionId || !transcriptPath) return null;
840
- if (!fs2.existsSync(transcriptPath)) return null;
863
+ if (!fs3.existsSync(transcriptPath)) return null;
841
864
  debug(`Got transcript from stdin: session=${sessionId}, path=${transcriptPath}`);
842
865
  return { sessionId, transcriptPath };
843
866
  } catch {
@@ -845,17 +868,17 @@ function readStdinPayload() {
845
868
  }
846
869
  }
847
870
  function findLatestTranscript() {
848
- const projectsDir = path2.join(os2.homedir(), ".claude", "projects");
849
- if (!fs2.existsSync(projectsDir)) return null;
871
+ const projectsDir = path3.join(os3.homedir(), ".claude", "projects");
872
+ if (!fs3.existsSync(projectsDir)) return null;
850
873
  let latestFile = null;
851
874
  let latestMtime = 0;
852
- for (const projEntry of fs2.readdirSync(projectsDir)) {
853
- const projDir = path2.join(projectsDir, projEntry);
854
- if (!fs2.statSync(projDir).isDirectory()) continue;
855
- for (const file of fs2.readdirSync(projDir)) {
875
+ for (const projEntry of fs3.readdirSync(projectsDir)) {
876
+ const projDir = path3.join(projectsDir, projEntry);
877
+ if (!fs3.statSync(projDir).isDirectory()) continue;
878
+ for (const file of fs3.readdirSync(projDir)) {
856
879
  if (!file.endsWith(".jsonl")) continue;
857
- const full = path2.join(projDir, file);
858
- const mtime = fs2.statSync(full).mtimeMs;
880
+ const full = path3.join(projDir, file);
881
+ const mtime = fs3.statSync(full).mtimeMs;
859
882
  if (mtime > latestMtime) {
860
883
  latestMtime = mtime;
861
884
  latestFile = full;
@@ -864,10 +887,10 @@ function findLatestTranscript() {
864
887
  }
865
888
  if (!latestFile) return null;
866
889
  try {
867
- const firstLine = fs2.readFileSync(latestFile, "utf-8").split("\n")[0];
890
+ const firstLine = fs3.readFileSync(latestFile, "utf-8").split("\n")[0];
868
891
  if (!firstLine) return null;
869
892
  const firstMsg = JSON.parse(firstLine);
870
- const sessionId = String(firstMsg.sessionId ?? path2.basename(latestFile, ".jsonl"));
893
+ const sessionId = String(firstMsg.sessionId ?? path3.basename(latestFile, ".jsonl"));
871
894
  return { sessionId, transcriptPath: latestFile };
872
895
  } catch {
873
896
  return null;
@@ -886,7 +909,7 @@ async function mainWorker() {
886
909
  debug(`Processing session: ${sessionId}`);
887
910
  let config = null;
888
911
  try {
889
- const content = fs2.readFileSync(transcriptPath, "utf-8");
912
+ const content = fs3.readFileSync(transcriptPath, "utf-8");
890
913
  const lines = content.split("\n");
891
914
  let cwd = "";
892
915
  for (const line of lines.slice(0, 5)) {
@@ -901,7 +924,7 @@ async function mainWorker() {
901
924
  }
902
925
  }
903
926
  if (cwd) {
904
- config = loadRespanConfig(path2.join(cwd, ".claude", "respan.json"));
927
+ config = loadRespanConfig(path3.join(cwd, ".claude", "respan.json"));
905
928
  }
906
929
  } catch {
907
930
  }
@@ -23,15 +23,37 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
25
  // src/hooks/codex-cli.ts
26
+ var fs3 = __toESM(require("node:fs"), 1);
27
+ var os3 = __toESM(require("node:os"), 1);
28
+ var path3 = __toESM(require("node:path"), 1);
29
+ var import_node_child_process = require("node:child_process");
30
+
31
+ // src/hooks/shared.ts
26
32
  var fs2 = __toESM(require("node:fs"), 1);
27
33
  var os2 = __toESM(require("node:os"), 1);
28
34
  var path2 = __toESM(require("node:path"), 1);
29
- var import_node_child_process = require("node:child_process");
30
35
 
31
- // src/hooks/shared.ts
36
+ // src/lib/config.ts
32
37
  var fs = __toESM(require("node:fs"), 1);
33
- var os = __toESM(require("node:os"), 1);
34
38
  var path = __toESM(require("node:path"), 1);
39
+ var os = __toESM(require("node:os"), 1);
40
+ var CONFIG_DIR = path.join(os.homedir(), ".respan");
41
+ var CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
42
+ var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
43
+ function restrictToOwner(target) {
44
+ if (process.platform === "win32") return;
45
+ try {
46
+ const mode = fs.statSync(target).mode & 511;
47
+ if (mode & 63) fs.chmodSync(target, mode & 448);
48
+ } catch {
49
+ }
50
+ }
51
+ function tightenCredentialPermissions() {
52
+ restrictToOwner(CONFIG_DIR);
53
+ restrictToOwner(CREDENTIALS_FILE);
54
+ }
55
+
56
+ // src/hooks/shared.ts
35
57
  function resolveTracesEndpoint(baseUrl) {
36
58
  const DEFAULT = "https://api.respan.ai/api/v2/traces";
37
59
  if (!baseUrl) return DEFAULT;
@@ -44,13 +66,13 @@ var _debug = false;
44
66
  function initLogging(logFile, debug2) {
45
67
  _logFile = logFile;
46
68
  _debug = debug2;
47
- const dir = path.dirname(logFile);
48
- fs.mkdirSync(dir, { recursive: true });
69
+ const dir = path2.dirname(logFile);
70
+ fs2.mkdirSync(dir, { recursive: true });
49
71
  }
50
72
  function log(level, message) {
51
73
  if (!_logFile) return;
52
74
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
53
- fs.appendFileSync(_logFile, `${ts} [${level}] ${message}
75
+ fs2.appendFileSync(_logFile, `${ts} [${level}] ${message}
54
76
  `);
55
77
  }
56
78
  function debug(message) {
@@ -61,14 +83,15 @@ function resolveCredentials() {
61
83
  let apiKey = process.env.RESPAN_API_KEY ?? "";
62
84
  let baseUrl = process.env.RESPAN_BASE_URL ?? DEFAULT_BASE_URL;
63
85
  if (!apiKey) {
64
- const credsFile = path.join(os.homedir(), ".respan", "credentials.json");
65
- if (fs.existsSync(credsFile)) {
86
+ const credsFile = path2.join(os2.homedir(), ".respan", "credentials.json");
87
+ if (fs2.existsSync(credsFile)) {
88
+ tightenCredentialPermissions();
66
89
  try {
67
- const creds = JSON.parse(fs.readFileSync(credsFile, "utf-8"));
68
- const configFile = path.join(os.homedir(), ".respan", "config.json");
90
+ const creds = JSON.parse(fs2.readFileSync(credsFile, "utf-8"));
91
+ const configFile = path2.join(os2.homedir(), ".respan", "config.json");
69
92
  let profile = "default";
70
- if (fs.existsSync(configFile)) {
71
- const cfg = JSON.parse(fs.readFileSync(configFile, "utf-8"));
93
+ if (fs2.existsSync(configFile)) {
94
+ const cfg = JSON.parse(fs2.readFileSync(configFile, "utf-8"));
72
95
  profile = cfg.activeProfile ?? "default";
73
96
  }
74
97
  const cred = creds[profile] ?? {};
@@ -98,11 +121,11 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
98
121
  "project_id"
99
122
  ]);
100
123
  function loadRespanConfig(configPath) {
101
- if (!fs.existsSync(configPath)) {
124
+ if (!fs2.existsSync(configPath)) {
102
125
  return { fields: {}, properties: {} };
103
126
  }
104
127
  try {
105
- const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
128
+ const raw = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
106
129
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
107
130
  return { fields: {}, properties: {} };
108
131
  }
@@ -122,36 +145,36 @@ function loadRespanConfig(configPath) {
122
145
  }
123
146
  }
124
147
  function loadState(statePath) {
125
- if (!fs.existsSync(statePath)) return {};
148
+ if (!fs2.existsSync(statePath)) return {};
126
149
  try {
127
- return JSON.parse(fs.readFileSync(statePath, "utf-8"));
150
+ return JSON.parse(fs2.readFileSync(statePath, "utf-8"));
128
151
  } catch {
129
152
  return {};
130
153
  }
131
154
  }
132
155
  function saveState(statePath, state) {
133
- const dir = path.dirname(statePath);
134
- fs.mkdirSync(dir, { recursive: true });
156
+ const dir = path2.dirname(statePath);
157
+ fs2.mkdirSync(dir, { recursive: true });
135
158
  const tmpPath = statePath + ".tmp." + process.pid;
136
159
  try {
137
- fs.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
138
- fs.renameSync(tmpPath, statePath);
160
+ fs2.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
161
+ fs2.renameSync(tmpPath, statePath);
139
162
  } catch (e) {
140
163
  try {
141
- fs.unlinkSync(tmpPath);
164
+ fs2.unlinkSync(tmpPath);
142
165
  } catch {
143
166
  }
144
- fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
167
+ fs2.writeFileSync(statePath, JSON.stringify(state, null, 2));
145
168
  }
146
169
  }
147
170
  function acquireLock(lockPath, timeoutMs = 5e3) {
148
171
  const deadline = Date.now() + timeoutMs;
149
172
  while (Date.now() < deadline) {
150
173
  try {
151
- fs.mkdirSync(lockPath);
174
+ fs2.mkdirSync(lockPath);
152
175
  return () => {
153
176
  try {
154
- fs.rmdirSync(lockPath);
177
+ fs2.rmdirSync(lockPath);
155
178
  } catch {
156
179
  }
157
180
  };
@@ -360,9 +383,9 @@ async function sendSpans(spans, apiKey, baseUrl, context) {
360
383
  const spanNames = spans.map((s) => s.span_name);
361
384
  debug(`Sending ${spans.length} spans (${body.length} bytes) to ${url} for ${context}: ${spanNames.join(", ")}`);
362
385
  if (_debug) {
363
- const debugDir = _logFile ? path.dirname(_logFile) : os.tmpdir();
364
- const debugFile = path.join(debugDir, `respan_spans_${context.replace(/\s+/g, "_")}.json`);
365
- fs.writeFileSync(debugFile, body);
386
+ const debugDir = _logFile ? path2.dirname(_logFile) : os2.tmpdir();
387
+ const debugFile = path2.join(debugDir, `respan_spans_${context.replace(/\s+/g, "_")}.json`);
388
+ fs2.writeFileSync(debugFile, body);
366
389
  debug(`Dumped OTLP payload to ${debugFile}`);
367
390
  }
368
391
  for (let attempt = 0; attempt < 3; attempt++) {
@@ -403,10 +426,10 @@ function sleep(ms) {
403
426
  }
404
427
 
405
428
  // src/hooks/codex-cli.ts
406
- var STATE_DIR = path2.join(os2.homedir(), ".codex", "state");
407
- var LOG_FILE = path2.join(STATE_DIR, "respan_hook.log");
408
- var STATE_FILE = path2.join(STATE_DIR, "respan_state.json");
409
- var LOCK_PATH = path2.join(STATE_DIR, "respan_hook.lock");
429
+ var STATE_DIR = path3.join(os3.homedir(), ".codex", "state");
430
+ var LOG_FILE = path3.join(STATE_DIR, "respan_hook.log");
431
+ var STATE_FILE = path3.join(STATE_DIR, "respan_state.json");
432
+ var LOCK_PATH = path3.join(STATE_DIR, "respan_hook.lock");
410
433
  var DEBUG_MODE = (process.env.CODEX_RESPAN_DEBUG ?? "").toLowerCase() === "true";
411
434
  var MAX_CHARS = parseInt(process.env.CODEX_RESPAN_MAX_CHARS ?? "4000", 10) || 4e3;
412
435
  initLogging(LOG_FILE, DEBUG_MODE);
@@ -656,13 +679,13 @@ function createSpans(sessionId, turnNum, turn, config) {
656
679
  return addDefaultsToAll(spans);
657
680
  }
658
681
  function findSessionFile(sessionId) {
659
- const sessionsDir = path2.join(os2.homedir(), ".codex", "sessions");
660
- if (!fs2.existsSync(sessionsDir)) return null;
682
+ const sessionsDir = path3.join(os3.homedir(), ".codex", "sessions");
683
+ if (!fs3.existsSync(sessionsDir)) return null;
661
684
  const walk = (dir) => {
662
- const entries = fs2.readdirSync(dir).sort().reverse();
685
+ const entries = fs3.readdirSync(dir).sort().reverse();
663
686
  for (const entry of entries) {
664
- const full = path2.join(dir, entry);
665
- if (fs2.statSync(full).isDirectory()) {
687
+ const full = path3.join(dir, entry);
688
+ if (fs3.statSync(full).isDirectory()) {
666
689
  const result = walk(full);
667
690
  if (result) return result;
668
691
  } else if (entry.endsWith(".jsonl") && entry.includes(sessionId)) {
@@ -674,14 +697,14 @@ function findSessionFile(sessionId) {
674
697
  return walk(sessionsDir);
675
698
  }
676
699
  function findLatestSessionFile() {
677
- const sessionsDir = path2.join(os2.homedir(), ".codex", "sessions");
678
- if (!fs2.existsSync(sessionsDir)) return null;
700
+ const sessionsDir = path3.join(os3.homedir(), ".codex", "sessions");
701
+ if (!fs3.existsSync(sessionsDir)) return null;
679
702
  let latestFile = null;
680
703
  let latestMtime = 0;
681
704
  const walk = (dir) => {
682
- for (const entry of fs2.readdirSync(dir)) {
683
- const full = path2.join(dir, entry);
684
- const stat = fs2.statSync(full);
705
+ for (const entry of fs3.readdirSync(dir)) {
706
+ const full = path3.join(dir, entry);
707
+ const stat = fs3.statSync(full);
685
708
  if (stat.isDirectory()) walk(full);
686
709
  else if (entry.endsWith(".jsonl") && stat.mtimeMs > latestMtime) {
687
710
  latestMtime = stat.mtimeMs;
@@ -692,11 +715,11 @@ function findLatestSessionFile() {
692
715
  walk(sessionsDir);
693
716
  if (!latestFile) return null;
694
717
  try {
695
- const firstLine = fs2.readFileSync(latestFile, "utf-8").split("\n")[0];
718
+ const firstLine = fs3.readFileSync(latestFile, "utf-8").split("\n")[0];
696
719
  if (!firstLine) return null;
697
720
  const firstMsg = JSON.parse(firstLine);
698
721
  const payload = firstMsg.payload ?? {};
699
- const sessionId = String(payload.id ?? path2.basename(latestFile, ".jsonl"));
722
+ const sessionId = String(payload.id ?? path3.basename(latestFile, ".jsonl"));
700
723
  return { sessionId, sessionFile: latestFile };
701
724
  } catch {
702
725
  return null;
@@ -713,7 +736,7 @@ async function mainWorker() {
713
736
  log("ERROR", "No API key");
714
737
  return;
715
738
  }
716
- const config = cwd ? loadRespanConfig(path2.join(cwd, ".codex", "respan.json")) : null;
739
+ const config = cwd ? loadRespanConfig(path3.join(cwd, ".codex", "respan.json")) : null;
717
740
  const maxAttempts = 3;
718
741
  let turns = 0;
719
742
  try {
@@ -723,7 +746,7 @@ async function mainWorker() {
723
746
  const state = loadState(STATE_FILE);
724
747
  const sessionState = state[sessionId] ?? {};
725
748
  const lastTurnCount = Number(sessionState.turn_count ?? 0);
726
- const lines = fs2.readFileSync(sessionFile, "utf-8").trim().split("\n");
749
+ const lines = fs3.readFileSync(sessionFile, "utf-8").trim().split("\n");
727
750
  const events = parseSession(lines);
728
751
  const allTurns = extractTurns(events);
729
752
  if (allTurns.length > lastTurnCount) {