@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.
- package/dist/hooks/claude-code.cjs +69 -46
- package/dist/hooks/codex-cli.cjs +68 -45
- package/dist/hooks/gemini-cli.cjs +67 -44
- package/dist/hooks/shared.d.ts +0 -1
- package/dist/hooks/shared.js +3 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.js +35 -7
- package/dist/lib/demo/prompt-fixtures.d.ts +0 -1
- package/oclif.manifest.json +142 -142
- package/package.json +1 -1
|
@@ -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/
|
|
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 =
|
|
48
|
-
|
|
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
|
-
|
|
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 =
|
|
65
|
-
if (
|
|
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(
|
|
68
|
-
const configFile =
|
|
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 (
|
|
71
|
-
const cfg = JSON.parse(
|
|
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 (!
|
|
124
|
+
if (!fs2.existsSync(configPath)) {
|
|
102
125
|
return { fields: {}, properties: {} };
|
|
103
126
|
}
|
|
104
127
|
try {
|
|
105
|
-
const raw = JSON.parse(
|
|
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 (!
|
|
148
|
+
if (!fs2.existsSync(statePath)) return {};
|
|
126
149
|
try {
|
|
127
|
-
return JSON.parse(
|
|
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 =
|
|
134
|
-
|
|
156
|
+
const dir = path2.dirname(statePath);
|
|
157
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
135
158
|
const tmpPath = statePath + ".tmp." + process.pid;
|
|
136
159
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
160
|
+
fs2.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
161
|
+
fs2.renameSync(tmpPath, statePath);
|
|
139
162
|
} catch (e) {
|
|
140
163
|
try {
|
|
141
|
-
|
|
164
|
+
fs2.unlinkSync(tmpPath);
|
|
142
165
|
} catch {
|
|
143
166
|
}
|
|
144
|
-
|
|
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
|
-
|
|
174
|
+
fs2.mkdirSync(lockPath);
|
|
152
175
|
return () => {
|
|
153
176
|
try {
|
|
154
|
-
|
|
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 ?
|
|
364
|
-
const debugFile =
|
|
365
|
-
|
|
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 =
|
|
407
|
-
var LOG_FILE =
|
|
408
|
-
var STATE_FILE =
|
|
409
|
-
var LOCK_PATH =
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
849
|
-
if (!
|
|
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
|
|
853
|
-
const projDir =
|
|
854
|
-
if (!
|
|
855
|
-
for (const file of
|
|
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 =
|
|
858
|
-
const mtime =
|
|
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 =
|
|
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 ??
|
|
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 =
|
|
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(
|
|
927
|
+
config = loadRespanConfig(path3.join(cwd, ".claude", "respan.json"));
|
|
905
928
|
}
|
|
906
929
|
} catch {
|
|
907
930
|
}
|
package/dist/hooks/codex-cli.cjs
CHANGED
|
@@ -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/
|
|
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 =
|
|
48
|
-
|
|
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
|
-
|
|
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 =
|
|
65
|
-
if (
|
|
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(
|
|
68
|
-
const configFile =
|
|
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 (
|
|
71
|
-
const cfg = JSON.parse(
|
|
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 (!
|
|
124
|
+
if (!fs2.existsSync(configPath)) {
|
|
102
125
|
return { fields: {}, properties: {} };
|
|
103
126
|
}
|
|
104
127
|
try {
|
|
105
|
-
const raw = JSON.parse(
|
|
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 (!
|
|
148
|
+
if (!fs2.existsSync(statePath)) return {};
|
|
126
149
|
try {
|
|
127
|
-
return JSON.parse(
|
|
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 =
|
|
134
|
-
|
|
156
|
+
const dir = path2.dirname(statePath);
|
|
157
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
135
158
|
const tmpPath = statePath + ".tmp." + process.pid;
|
|
136
159
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
160
|
+
fs2.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
161
|
+
fs2.renameSync(tmpPath, statePath);
|
|
139
162
|
} catch (e) {
|
|
140
163
|
try {
|
|
141
|
-
|
|
164
|
+
fs2.unlinkSync(tmpPath);
|
|
142
165
|
} catch {
|
|
143
166
|
}
|
|
144
|
-
|
|
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
|
-
|
|
174
|
+
fs2.mkdirSync(lockPath);
|
|
152
175
|
return () => {
|
|
153
176
|
try {
|
|
154
|
-
|
|
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 ?
|
|
364
|
-
const debugFile =
|
|
365
|
-
|
|
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 =
|
|
407
|
-
var LOG_FILE =
|
|
408
|
-
var STATE_FILE =
|
|
409
|
-
var LOCK_PATH =
|
|
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 =
|
|
660
|
-
if (!
|
|
682
|
+
const sessionsDir = path3.join(os3.homedir(), ".codex", "sessions");
|
|
683
|
+
if (!fs3.existsSync(sessionsDir)) return null;
|
|
661
684
|
const walk = (dir) => {
|
|
662
|
-
const entries =
|
|
685
|
+
const entries = fs3.readdirSync(dir).sort().reverse();
|
|
663
686
|
for (const entry of entries) {
|
|
664
|
-
const full =
|
|
665
|
-
if (
|
|
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 =
|
|
678
|
-
if (!
|
|
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
|
|
683
|
-
const full =
|
|
684
|
-
const stat =
|
|
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 =
|
|
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 ??
|
|
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(
|
|
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 =
|
|
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) {
|