@triflux/core 10.31.0 → 10.32.0
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/hooks/codex-session-hook.mjs +113 -0
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { drainPendingSynapse as defaultDrainPendingSynapse } from "../hub/team/synapse-http.mjs";
|
|
6
|
+
import {
|
|
7
|
+
heartbeatInteractiveSession as defaultHeartbeatInteractiveSession,
|
|
8
|
+
registerInteractiveSession as defaultRegisterInteractiveSession,
|
|
9
|
+
} from "./session-start-fast.mjs";
|
|
10
|
+
|
|
11
|
+
// hub-ensure is loaded lazily so the byte-identical packages/core mirror of this
|
|
12
|
+
// file loads cleanly. packages/core mirrors scripts/lib only (not scripts/*), so a
|
|
13
|
+
// static `../scripts/hub-ensure.mjs` import would make the core copy throw
|
|
14
|
+
// ERR_MODULE_NOT_FOUND at load time. The core copy is published-but-dormant — the
|
|
15
|
+
// live codex hook always runs from the installed triflux package where the path
|
|
16
|
+
// resolves. This mirrors session-start-fast.mjs's dynamic script-import pattern.
|
|
17
|
+
async function defaultHubEnsureRun(stdinData) {
|
|
18
|
+
const { run } = await import(
|
|
19
|
+
new URL("../scripts/hub-ensure.mjs", import.meta.url).href
|
|
20
|
+
);
|
|
21
|
+
return run(stdinData);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parsePayload(stdinData) {
|
|
25
|
+
try {
|
|
26
|
+
const raw = typeof stdinData === "string" ? stdinData : "";
|
|
27
|
+
return raw.trim()
|
|
28
|
+
? { ok: true, payload: JSON.parse(raw) }
|
|
29
|
+
: { ok: false, payload: {} };
|
|
30
|
+
} catch {
|
|
31
|
+
return { ok: false, payload: {} };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeMode(mode, payload) {
|
|
36
|
+
const direct = String(mode || "")
|
|
37
|
+
.trim()
|
|
38
|
+
.toLowerCase();
|
|
39
|
+
if (direct === "register" || direct === "heartbeat") return direct;
|
|
40
|
+
|
|
41
|
+
const eventName = String(payload?.hook_event_name || "")
|
|
42
|
+
.trim()
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.replace(/-/g, "_");
|
|
45
|
+
if (eventName === "session_start" || eventName === "sessionstart") {
|
|
46
|
+
return "register";
|
|
47
|
+
}
|
|
48
|
+
if (eventName === "user_prompt_submit" || eventName === "userpromptsubmit") {
|
|
49
|
+
return "heartbeat";
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function runCodexSessionHook(stdinData, opts = {}) {
|
|
55
|
+
const output = "{}\n";
|
|
56
|
+
const parsed = parsePayload(stdinData);
|
|
57
|
+
const mode = parsed.ok
|
|
58
|
+
? normalizeMode(opts.argvMode ?? argv[2], parsed.payload)
|
|
59
|
+
: "";
|
|
60
|
+
const hubEnsureRun = opts.hubEnsureRun || defaultHubEnsureRun;
|
|
61
|
+
const registerInteractiveSession =
|
|
62
|
+
opts.registerInteractiveSession || defaultRegisterInteractiveSession;
|
|
63
|
+
const heartbeatInteractiveSession =
|
|
64
|
+
opts.heartbeatInteractiveSession || defaultHeartbeatInteractiveSession;
|
|
65
|
+
const drainPendingSynapse =
|
|
66
|
+
opts.drainPendingSynapse || defaultDrainPendingSynapse;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
if (mode === "register") {
|
|
70
|
+
try {
|
|
71
|
+
await hubEnsureRun(stdinData);
|
|
72
|
+
} catch {}
|
|
73
|
+
try {
|
|
74
|
+
registerInteractiveSession(stdinData);
|
|
75
|
+
} catch {}
|
|
76
|
+
try {
|
|
77
|
+
await drainPendingSynapse(1000);
|
|
78
|
+
} catch {}
|
|
79
|
+
} else if (mode === "heartbeat") {
|
|
80
|
+
try {
|
|
81
|
+
heartbeatInteractiveSession(stdinData);
|
|
82
|
+
} catch {}
|
|
83
|
+
try {
|
|
84
|
+
await drainPendingSynapse(500);
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Codex session hooks are observational and must never block the session.
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (opts.writeStdout !== false) {
|
|
92
|
+
stdout.write(output);
|
|
93
|
+
}
|
|
94
|
+
return output;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readStdin() {
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
let data = "";
|
|
100
|
+
stdin.setEncoding("utf8");
|
|
101
|
+
stdin.on("data", (chunk) => {
|
|
102
|
+
data += chunk;
|
|
103
|
+
});
|
|
104
|
+
stdin.on("end", () => resolve(data));
|
|
105
|
+
stdin.on("error", () => resolve(data));
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (argv[1] && import.meta.url === pathToFileURL(argv[1]).href) {
|
|
110
|
+
const stdinData = await readStdin();
|
|
111
|
+
await runCodexSessionHook(stdinData);
|
|
112
|
+
exit(0);
|
|
113
|
+
}
|