@triflux/core 10.31.0 → 10.33.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
- package/scripts/lib/env-probe.mjs +35 -8
|
@@ -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
|
+
}
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { resolveHubPortForContext } from "../../hub/hub-lifecycle.mjs";
|
|
7
8
|
import { whichCommand, whichCommandAsync } from "../../hub/platform.mjs";
|
|
8
9
|
|
|
9
10
|
const HUB_DEFAULT_PORT = 27888;
|
|
@@ -37,13 +38,36 @@ function fetchHubStatus({
|
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
export function resolveDefaultStatusUrl(env = process.env) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
export function resolveDefaultStatusUrl(env = process.env, cwd = process.cwd()) {
|
|
42
|
+
const port = resolveHubPortForContext({
|
|
43
|
+
env,
|
|
44
|
+
cwd,
|
|
45
|
+
defaultPort: HUB_DEFAULT_PORT,
|
|
46
|
+
});
|
|
44
47
|
return `http://127.0.0.1:${port}/status`;
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
function resolveStatusUrlForContext({
|
|
51
|
+
statusUrl,
|
|
52
|
+
env = process.env,
|
|
53
|
+
cwd = process.cwd(),
|
|
54
|
+
} = {}) {
|
|
55
|
+
try {
|
|
56
|
+
const url = new URL(String(statusUrl));
|
|
57
|
+
url.port = String(
|
|
58
|
+
resolveHubPortForContext({
|
|
59
|
+
port: url.port,
|
|
60
|
+
env,
|
|
61
|
+
cwd,
|
|
62
|
+
defaultPort: HUB_DEFAULT_PORT,
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
return url.toString();
|
|
66
|
+
} catch {
|
|
67
|
+
return resolveDefaultStatusUrl(env, cwd);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
47
71
|
function normalizeCliName(name) {
|
|
48
72
|
return String(name ?? "").trim() || null;
|
|
49
73
|
}
|
|
@@ -212,8 +236,10 @@ export function detectCodexPlan(options = {}) {
|
|
|
212
236
|
}
|
|
213
237
|
|
|
214
238
|
export function checkHub({
|
|
239
|
+
env = process.env,
|
|
240
|
+
cwd = process.cwd(),
|
|
215
241
|
pkgRoot = DEFAULT_PKG_ROOT,
|
|
216
|
-
statusUrl = resolveDefaultStatusUrl(),
|
|
242
|
+
statusUrl = resolveDefaultStatusUrl(env, cwd),
|
|
217
243
|
restart = true,
|
|
218
244
|
requestTimeoutMs = 3000,
|
|
219
245
|
pollAttempts = 8,
|
|
@@ -223,10 +249,11 @@ export function checkHub({
|
|
|
223
249
|
existsSyncFn = existsSync,
|
|
224
250
|
sleepSyncFn = sleepSync,
|
|
225
251
|
} = {}) {
|
|
252
|
+
const guardedStatusUrl = resolveStatusUrlForContext({ statusUrl, env, cwd });
|
|
226
253
|
try {
|
|
227
254
|
return fetchHubStatus({
|
|
228
255
|
execSyncFn,
|
|
229
|
-
statusUrl,
|
|
256
|
+
statusUrl: guardedStatusUrl,
|
|
230
257
|
timeout: requestTimeoutMs,
|
|
231
258
|
});
|
|
232
259
|
} catch {}
|
|
@@ -239,7 +266,7 @@ export function checkHub({
|
|
|
239
266
|
|
|
240
267
|
try {
|
|
241
268
|
const child = spawnFn(process.execPath, [serverPath], {
|
|
242
|
-
env: { ...
|
|
269
|
+
env: { ...env, TFX_HUB_PORT: String(new URL(guardedStatusUrl).port) },
|
|
243
270
|
detached: true,
|
|
244
271
|
stdio: "ignore",
|
|
245
272
|
windowsHide: true,
|
|
@@ -254,7 +281,7 @@ export function checkHub({
|
|
|
254
281
|
try {
|
|
255
282
|
const status = fetchHubStatus({
|
|
256
283
|
execSyncFn,
|
|
257
|
-
statusUrl,
|
|
284
|
+
statusUrl: guardedStatusUrl,
|
|
258
285
|
timeout: Math.min(requestTimeoutMs, 1000),
|
|
259
286
|
});
|
|
260
287
|
if (status.state === "healthy") {
|