kojee-mcp 0.7.1 → 0.7.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/{chunk-KPMD72FY.js → chunk-6XWTUDWW.js} +65 -26
- package/dist/{chunk-GNLCUJBK.js → chunk-A4IOKD4Z.js} +100 -2
- package/dist/{chunk-FBJCPRVH.js → chunk-ABECLEYE.js} +62 -11
- package/dist/{chunk-SSW5AQSR.js → chunk-FJUAMJHU.js} +3 -6
- package/dist/{chunk-FSKGQ6GT.js → chunk-HI42GBQ3.js} +5 -0
- package/dist/chunk-IZN7IZPW.js +132 -0
- package/dist/{chunk-UPJV7GBE.js → chunk-TCWIXG5C.js} +1 -1
- package/dist/{chunk-QJFMU4QC.js → chunk-TMCNB4JH.js} +1 -1
- package/dist/chunk-XLRF5ATG.js +103 -0
- package/dist/{chunk-UFHGZUST.js → chunk-XPIW4N55.js} +22 -14
- package/dist/cli.js +23 -14
- package/dist/codex-prompt-submit-hook-J5PZEJSK.js +39 -0
- package/dist/codex-stop-hook-32W4BIOM.js +136 -0
- package/dist/{connect-handler-4DRTFMOB.js → connect-handler-NZINEMG3.js} +14 -7
- package/dist/{doctor-5QJ3HGNR.js → doctor-WUU5BVPT.js} +2 -2
- package/dist/doctor-codex-PJFWIABF.js +370 -0
- package/dist/{event-stream-KRYWEYWO.js → event-stream-WPN3EN7C.js} +5 -1
- package/dist/index.js +5 -5
- package/dist/{install-V7LSQCYZ.js → install-JQNDGAAQ.js} +14 -2
- package/dist/lib.d.ts +40 -15
- package/dist/lib.js +7 -7
- package/dist/pending-state-6TVRR63P.js +134 -0
- package/dist/{registry-ZZZ26WGA.js → registry-2QS42EQF.js} +114 -51
- package/dist/{server-ITPFQVTK.js → server-DU3LFS32.js} +4 -2
- package/dist/{setup-handler-JFM45NCN.js → setup-handler-44ASXYMS.js} +7 -7
- package/dist/{stop-hook-5ABGTC2O.js → stop-hook-N6TX4YQT.js} +2 -2
- package/dist/{tail-stream-NBGHHBS4.js → tail-stream-N43D53RC.js} +99 -19
- package/package.json +1 -1
- package/skills/using-tandems/SKILL.md +2 -2
- package/dist/chunk-EBYUJM3H.js +0 -14
- package/dist/codex-stop-hook-BMOJVM6O.js +0 -96
- package/dist/doctor-codex-VGJKTX2E.js +0 -163
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// src/delivery/codex-status.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import fsp from "fs/promises";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import crypto from "crypto";
|
|
7
|
+
var CODEX_STATUS_HEARTBEAT_MS = 6e4;
|
|
8
|
+
var CODEX_STATUS_STALE_MS = 5 * 6e4;
|
|
9
|
+
function codexStatusDir() {
|
|
10
|
+
return path.join(os.homedir(), ".kojee");
|
|
11
|
+
}
|
|
12
|
+
function codexStatusPath(pid = process.pid) {
|
|
13
|
+
return path.join(codexStatusDir(), `codex-status-${pid}.json`);
|
|
14
|
+
}
|
|
15
|
+
var STATUS_FILE_RE = /^codex-status(?:-(\d+))?\.json$/;
|
|
16
|
+
function readAllCodexStatusRaw() {
|
|
17
|
+
const dir = codexStatusDir();
|
|
18
|
+
let names;
|
|
19
|
+
try {
|
|
20
|
+
names = fs.readdirSync(dir);
|
|
21
|
+
} catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const name of names.filter((n) => STATUS_FILE_RE.test(n)).sort()) {
|
|
26
|
+
try {
|
|
27
|
+
out.push(fs.readFileSync(path.join(dir, name), "utf8"));
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
async function sweepDeadCodexStatusFiles(isPidAlive = defaultIsPidAlive) {
|
|
34
|
+
const dir = codexStatusDir();
|
|
35
|
+
let names;
|
|
36
|
+
try {
|
|
37
|
+
names = await fsp.readdir(dir);
|
|
38
|
+
} catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
const m = STATUS_FILE_RE.exec(name);
|
|
43
|
+
if (!m) continue;
|
|
44
|
+
const full = path.join(dir, name);
|
|
45
|
+
let pid = m[1] ? Number(m[1]) : null;
|
|
46
|
+
if (pid === null) {
|
|
47
|
+
try {
|
|
48
|
+
pid = readCodexStatus(await fsp.readFile(full, "utf8"))?.pid ?? null;
|
|
49
|
+
} catch {
|
|
50
|
+
pid = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (pid === process.pid) continue;
|
|
54
|
+
if (pid !== null && isPidAlive(pid)) continue;
|
|
55
|
+
try {
|
|
56
|
+
await fsp.unlink(full);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function defaultIsPidAlive(pid) {
|
|
62
|
+
try {
|
|
63
|
+
process.kill(pid, 0);
|
|
64
|
+
return true;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return err?.code === "EPERM";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function readCodexStatus(raw) {
|
|
70
|
+
if (!raw) return null;
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(raw);
|
|
73
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || typeof parsed.pid !== "number" || typeof parsed.updated_at !== "string" || typeof parsed.stream !== "object" || parsed.stream === null) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function writeCodexStatus(status) {
|
|
82
|
+
const statusPath = codexStatusPath(status.pid);
|
|
83
|
+
const tmp = `${statusPath}.tmp-${process.pid}-${crypto.randomUUID()}`;
|
|
84
|
+
try {
|
|
85
|
+
await fsp.mkdir(path.dirname(statusPath), { recursive: true });
|
|
86
|
+
await fsp.writeFile(tmp, JSON.stringify(status, null, 2), { mode: 384 });
|
|
87
|
+
await fsp.rename(tmp, statusPath);
|
|
88
|
+
} catch {
|
|
89
|
+
try {
|
|
90
|
+
await fsp.unlink(tmp);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export {
|
|
97
|
+
CODEX_STATUS_HEARTBEAT_MS,
|
|
98
|
+
CODEX_STATUS_STALE_MS,
|
|
99
|
+
readAllCodexStatusRaw,
|
|
100
|
+
sweepDeadCodexStatusFiles,
|
|
101
|
+
readCodexStatus,
|
|
102
|
+
writeCodexStatus
|
|
103
|
+
};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
removeOpenclawMcpServer,
|
|
3
|
-
writeOpenclawMcpConfig
|
|
4
|
-
} from "./chunk-E6WMFMM2.js";
|
|
5
1
|
import {
|
|
6
2
|
buildCodexMcpServerTable,
|
|
3
|
+
buildCodexPromptSubmitHookBlock,
|
|
7
4
|
buildCodexStopHookBlock,
|
|
8
5
|
isPlaceholderWebhookUrl,
|
|
9
6
|
removeCodexConfig,
|
|
10
7
|
writeCodexConfig
|
|
11
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6XWTUDWW.js";
|
|
9
|
+
import {
|
|
10
|
+
removeOpenclawMcpServer,
|
|
11
|
+
writeOpenclawMcpConfig
|
|
12
|
+
} from "./chunk-E6WMFMM2.js";
|
|
13
|
+
import {
|
|
14
|
+
WIZARD_RUNTIMES,
|
|
15
|
+
isWizardRuntime
|
|
16
|
+
} from "./chunk-77HWBSRH.js";
|
|
12
17
|
import {
|
|
13
18
|
CONNECT_RUNTIMES
|
|
14
19
|
} from "./chunk-EIH2LNF4.js";
|
|
@@ -20,10 +25,6 @@ import {
|
|
|
20
25
|
import {
|
|
21
26
|
kojeeHomeDir
|
|
22
27
|
} from "./chunk-SQL56SEB.js";
|
|
23
|
-
import {
|
|
24
|
-
WIZARD_RUNTIMES,
|
|
25
|
-
isWizardRuntime
|
|
26
|
-
} from "./chunk-77HWBSRH.js";
|
|
27
28
|
import {
|
|
28
29
|
resolveSignatureEmission,
|
|
29
30
|
resolveWebhookConfig
|
|
@@ -48,7 +49,7 @@ import {
|
|
|
48
49
|
CODEX_LISTEN_CAP_MS,
|
|
49
50
|
buildCondensedTandemRules,
|
|
50
51
|
buildWebhookReceiverNote
|
|
51
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-FJUAMJHU.js";
|
|
52
53
|
|
|
53
54
|
// src/wizard/pair-slot.ts
|
|
54
55
|
import fs from "fs";
|
|
@@ -789,7 +790,7 @@ function whatHappensNext(runtime, opts) {
|
|
|
789
790
|
return lines.join("\n");
|
|
790
791
|
}
|
|
791
792
|
async function configureClaudeCode(opts) {
|
|
792
|
-
const { runInit } = await import("./install-
|
|
793
|
+
const { runInit } = await import("./install-JQNDGAAQ.js");
|
|
793
794
|
const report = runInit({
|
|
794
795
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
795
796
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
@@ -853,7 +854,7 @@ function configureCodex(opts) {
|
|
|
853
854
|
const secret = connectPairedMode || !hasRealUrl ? void 0 : wh.secret || generateWebhookSecret();
|
|
854
855
|
const tokenArgs = opts.token && opts.url ? { token: opts.token, url: opts.url } : {};
|
|
855
856
|
const pairedArgs = opts.pairedConfigPath ? { pairedConfigPath: opts.pairedConfigPath } : {};
|
|
856
|
-
writeCodexConfig({
|
|
857
|
+
const writeResult = writeCodexConfig({
|
|
857
858
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
858
859
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
859
860
|
...url !== void 0 ? { webhookUrl: url } : {},
|
|
@@ -885,9 +886,16 @@ function configureCodex(opts) {
|
|
|
885
886
|
})));
|
|
886
887
|
if (wh.warning) lines.push(`webhook WARNING: ${wh.warning}`);
|
|
887
888
|
lines.push("");
|
|
888
|
-
lines.push("Wrote the Codex Stop
|
|
889
|
+
lines.push("Wrote the Codex Stop + UserPromptSubmit hooks (~/.codex/hooks.json; inline TOML form):");
|
|
889
890
|
lines.push(indent(buildCodexStopHookBlock()));
|
|
891
|
+
lines.push(indent(buildCodexPromptSubmitHookBlock()));
|
|
890
892
|
lines.push("");
|
|
893
|
+
if (writeResult.hooksChanged) {
|
|
894
|
+
lines.push(
|
|
895
|
+
"IMPORTANT: Codex will re-prompt to trust hooks on its next launch \u2014 approve it, or wakes silently stop."
|
|
896
|
+
);
|
|
897
|
+
lines.push("");
|
|
898
|
+
}
|
|
891
899
|
lines.push(
|
|
892
900
|
connectPairedMode ? "webhook: (unchanged \u2014 `connect` rewrites only the credential args, not the wake path)" : `webhook: ${wh.url ? wh.redactedSummary : "OPTIONAL \u2014 not configured (wake is self-contained; set KOJEE_WEBHOOK_URL only for an extra low-latency push path)"}`
|
|
893
901
|
);
|
|
@@ -1027,7 +1035,7 @@ async function runWizardUninstall(runtime, opts) {
|
|
|
1027
1035
|
const effective = opts.runtime !== void 0 ? runtime : readRecordedRuntime() ?? runtime;
|
|
1028
1036
|
const lines = [`Uninstalling runtime: ${effective}`];
|
|
1029
1037
|
if (effective === "claude-code") {
|
|
1030
|
-
const { runUninstall } = await import("./install-
|
|
1038
|
+
const { runUninstall } = await import("./install-JQNDGAAQ.js");
|
|
1031
1039
|
const report = runUninstall({
|
|
1032
1040
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
1033
1041
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {}
|
package/dist/cli.js
CHANGED
|
@@ -4,25 +4,25 @@ import {
|
|
|
4
4
|
} from "./chunk-EIAUW6KO.js";
|
|
5
5
|
import {
|
|
6
6
|
startProxy
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-ABECLEYE.js";
|
|
8
|
+
import "./chunk-TCWIXG5C.js";
|
|
9
9
|
import {
|
|
10
10
|
pairedConfigPath
|
|
11
11
|
} from "./chunk-5SZHXYPK.js";
|
|
12
12
|
import "./chunk-247WFMCJ.js";
|
|
13
|
-
import "./chunk-I67C2HYA.js";
|
|
14
13
|
import "./chunk-Z5LPNJQ6.js";
|
|
14
|
+
import "./chunk-I67C2HYA.js";
|
|
15
15
|
import "./chunk-MIEI4PLB.js";
|
|
16
16
|
import {
|
|
17
17
|
defaultPairedKeystorePath,
|
|
18
18
|
deriveKeystorePath
|
|
19
19
|
} from "./chunk-6G6YYST6.js";
|
|
20
20
|
import "./chunk-U5HHHRXA.js";
|
|
21
|
-
import "./chunk-
|
|
21
|
+
import "./chunk-A4IOKD4Z.js";
|
|
22
22
|
import {
|
|
23
23
|
VERSION
|
|
24
24
|
} from "./chunk-5DHIUN73.js";
|
|
25
|
-
import "./chunk-
|
|
25
|
+
import "./chunk-FJUAMJHU.js";
|
|
26
26
|
import "./chunk-PPTKGWFF.js";
|
|
27
27
|
import "./chunk-XJEBJIQE.js";
|
|
28
28
|
import "./chunk-KNEJTD6G.js";
|
|
@@ -48,7 +48,7 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
48
48
|
program.command("connect <code>").description(
|
|
49
49
|
"Connect this runtime to Kojee with a per-agent pair code from the dashboard (claude-code | codex | openclaw | hermes). claude-code/codex/openclaw write a per-runtime paired slot (~/.kojee/agents/<runtime>/config.json) and point the runtime launcher at it via --paired-config; hermes writes the global ~/.kojee/config.json (its daemon + `kojee-mcp send` read that). Idempotent."
|
|
50
50
|
).requiredOption("--runtime <id>", "Target runtime: claude-code | codex | openclaw | hermes").option("--url <url>", "Broker base URL (default: the canonical staging broker)").action(async (code, opts) => {
|
|
51
|
-
const { runConnect } = await import("./connect-handler-
|
|
51
|
+
const { runConnect } = await import("./connect-handler-NZINEMG3.js");
|
|
52
52
|
const result = await runConnect({
|
|
53
53
|
code,
|
|
54
54
|
runtime: opts.runtime,
|
|
@@ -58,9 +58,12 @@ program.command("connect <code>").description(
|
|
|
58
58
|
console.error(result.output);
|
|
59
59
|
process.exit(result.exitCode);
|
|
60
60
|
});
|
|
61
|
-
program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption(
|
|
61
|
+
program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption(
|
|
62
|
+
"--type <type>",
|
|
63
|
+
"Hook type: stop, user-prompt-submit, codex-stop, or codex-prompt-submit"
|
|
64
|
+
).action(async (opts) => {
|
|
62
65
|
if (opts.type === "stop") {
|
|
63
|
-
const { runStopHook } = await import("./stop-hook-
|
|
66
|
+
const { runStopHook } = await import("./stop-hook-N6TX4YQT.js");
|
|
64
67
|
await runStopHook();
|
|
65
68
|
process.exit(0);
|
|
66
69
|
} else if (opts.type === "user-prompt-submit") {
|
|
@@ -68,16 +71,22 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
|
|
|
68
71
|
await runUserPromptSubmitHook();
|
|
69
72
|
process.exit(0);
|
|
70
73
|
} else if (opts.type === "codex-stop") {
|
|
71
|
-
const { runCodexStopHook } = await import("./codex-stop-hook-
|
|
74
|
+
const { runCodexStopHook } = await import("./codex-stop-hook-32W4BIOM.js");
|
|
72
75
|
await runCodexStopHook();
|
|
73
76
|
process.exit(0);
|
|
77
|
+
} else if (opts.type === "codex-prompt-submit") {
|
|
78
|
+
const { runCodexPromptSubmitHook } = await import("./codex-prompt-submit-hook-J5PZEJSK.js");
|
|
79
|
+
await runCodexPromptSubmitHook();
|
|
80
|
+
process.exit(0);
|
|
74
81
|
} else {
|
|
75
|
-
console.error(
|
|
82
|
+
console.error(
|
|
83
|
+
`Unknown hook type: ${opts.type}. Expected 'stop', 'user-prompt-submit', 'codex-stop', or 'codex-prompt-submit'.`
|
|
84
|
+
);
|
|
76
85
|
process.exit(1);
|
|
77
86
|
}
|
|
78
87
|
});
|
|
79
88
|
program.command("install-hooks").description("Install kojee Stop + UserPromptSubmit hooks in ~/.claude/settings.json (idempotent)").option("--hooks-path <path>", "Override default ~/.claude/settings.json").option("--uninstall", "Remove kojee hook entries instead of installing them").action(async (opts) => {
|
|
80
|
-
const { installHooks, uninstallHooks } = await import("./install-
|
|
89
|
+
const { installHooks, uninstallHooks } = await import("./install-JQNDGAAQ.js");
|
|
81
90
|
if (opts.uninstall) {
|
|
82
91
|
const removed = uninstallHooks({ hooksPath: opts.hooksPath });
|
|
83
92
|
console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
|
|
@@ -106,7 +115,7 @@ program.command("send <tandem_id>").description(
|
|
|
106
115
|
process.exit(exitCode);
|
|
107
116
|
});
|
|
108
117
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
109
|
-
const { runTail } = await import("./tail-stream-
|
|
118
|
+
const { runTail } = await import("./tail-stream-N43D53RC.js");
|
|
110
119
|
try {
|
|
111
120
|
await runTail(filePath);
|
|
112
121
|
} catch (err) {
|
|
@@ -115,7 +124,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
115
124
|
}
|
|
116
125
|
});
|
|
117
126
|
program.command("doctor").description("Diagnose the kojee wake path (proxy, hook-server, SSE stream, event log, Monitor) and print the exact wake recipe").action(async () => {
|
|
118
|
-
const { runDoctor } = await import("./doctor-
|
|
127
|
+
const { runDoctor } = await import("./doctor-WUU5BVPT.js");
|
|
119
128
|
const code = await runDoctor();
|
|
120
129
|
process.exit(code);
|
|
121
130
|
});
|
|
@@ -134,7 +143,7 @@ function addInstallOptions(cmd, runtimeHelp) {
|
|
|
134
143
|
function makeInstallAction(verb) {
|
|
135
144
|
return async (opts) => {
|
|
136
145
|
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
137
|
-
const { runSetup, resolvePairCode } = await import("./setup-handler-
|
|
146
|
+
const { runSetup, resolvePairCode } = await import("./setup-handler-44ASXYMS.js");
|
|
138
147
|
const pairCode = resolvePairCode(opts);
|
|
139
148
|
const result = await runSetup({
|
|
140
149
|
verb,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readHookStdin
|
|
3
|
+
} from "./chunk-LSUB6QMP.js";
|
|
4
|
+
import {
|
|
5
|
+
listPending
|
|
6
|
+
} from "./chunk-IZN7IZPW.js";
|
|
7
|
+
import {
|
|
8
|
+
CODEX_WAKE_BELL
|
|
9
|
+
} from "./chunk-FJUAMJHU.js";
|
|
10
|
+
|
|
11
|
+
// src/hooks/codex-prompt-submit-hook.ts
|
|
12
|
+
function buildCodexPromptSubmitOutput() {
|
|
13
|
+
let pending;
|
|
14
|
+
try {
|
|
15
|
+
pending = listPending();
|
|
16
|
+
} catch {
|
|
17
|
+
return "{}";
|
|
18
|
+
}
|
|
19
|
+
if (pending.length === 0) return "{}";
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
hookSpecificOutput: {
|
|
22
|
+
hookEventName: "UserPromptSubmit",
|
|
23
|
+
additionalContext: CODEX_WAKE_BELL
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async function runCodexPromptSubmitHook() {
|
|
28
|
+
await readHookStdin();
|
|
29
|
+
let out = "{}";
|
|
30
|
+
try {
|
|
31
|
+
out = buildCodexPromptSubmitOutput();
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write(out);
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
buildCodexPromptSubmitOutput,
|
|
38
|
+
runCodexPromptSubmitHook
|
|
39
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readHookStdin
|
|
3
|
+
} from "./chunk-LSUB6QMP.js";
|
|
4
|
+
import {
|
|
5
|
+
drainedRoomsPath,
|
|
6
|
+
listPending,
|
|
7
|
+
pendingRoomsPath
|
|
8
|
+
} from "./chunk-IZN7IZPW.js";
|
|
9
|
+
import {
|
|
10
|
+
CODEX_WAKE_BELL
|
|
11
|
+
} from "./chunk-FJUAMJHU.js";
|
|
12
|
+
|
|
13
|
+
// src/hooks/codex-stop-hook.ts
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import path2 from "path";
|
|
16
|
+
|
|
17
|
+
// src/hooks/codex-pending-path.ts
|
|
18
|
+
import path from "path";
|
|
19
|
+
function codexNudgeAttemptsPath() {
|
|
20
|
+
return path.join(path.dirname(pendingRoomsPath()), "codex-nudge-attempts");
|
|
21
|
+
}
|
|
22
|
+
function codexWakeDropLogPath() {
|
|
23
|
+
return path.join(path.dirname(pendingRoomsPath()), "codex-pending-wake-drops.log");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/hooks/codex-stop-hook.ts
|
|
27
|
+
var CODEX_PEEK_MS = clampPeekMs(
|
|
28
|
+
Number.parseInt(process.env["KOJEE_CODEX_PEEK_MS"] ?? "150", 10)
|
|
29
|
+
);
|
|
30
|
+
function clampPeekMs(raw) {
|
|
31
|
+
if (!Number.isFinite(raw) || raw <= 0) return 150;
|
|
32
|
+
return Math.min(raw, 500);
|
|
33
|
+
}
|
|
34
|
+
var MAX_NUDGES_PER_ROOM = 3;
|
|
35
|
+
var ANON_KEY = "-";
|
|
36
|
+
function readNudgeAttempts() {
|
|
37
|
+
const out = /* @__PURE__ */ new Map();
|
|
38
|
+
let raw;
|
|
39
|
+
try {
|
|
40
|
+
raw = fs.readFileSync(codexNudgeAttemptsPath(), "utf8");
|
|
41
|
+
} catch {
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
for (const line of raw.split("\n")) {
|
|
45
|
+
const parts = line.trim().split(/\s+/);
|
|
46
|
+
if (parts.length !== 3) continue;
|
|
47
|
+
const cursor = Number.parseInt(parts[1], 10);
|
|
48
|
+
const count = Number.parseInt(parts[2], 10);
|
|
49
|
+
if (!Number.isFinite(cursor) || !Number.isFinite(count) || count < 0) continue;
|
|
50
|
+
out.set(parts[0], { cursor, count });
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
function writeNudgeAttempts(attempts) {
|
|
55
|
+
const filePath = codexNudgeAttemptsPath();
|
|
56
|
+
const tmp = `${filePath}.tmp-${process.pid}`;
|
|
57
|
+
try {
|
|
58
|
+
fs.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
59
|
+
const body = Array.from(attempts.entries()).map(([key, rec]) => `${key} ${rec.cursor} ${rec.count}`).join("\n");
|
|
60
|
+
fs.writeFileSync(tmp, body === "" ? "" : body + "\n", { mode: 384 });
|
|
61
|
+
fs.renameSync(tmp, filePath);
|
|
62
|
+
} catch {
|
|
63
|
+
try {
|
|
64
|
+
fs.unlinkSync(tmp);
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function logCappedRoom(key, cursor, attempts) {
|
|
70
|
+
try {
|
|
71
|
+
const logPath = codexWakeDropLogPath();
|
|
72
|
+
fs.mkdirSync(path2.dirname(logPath), { recursive: true });
|
|
73
|
+
fs.appendFileSync(
|
|
74
|
+
logPath,
|
|
75
|
+
`${(/* @__PURE__ */ new Date()).toISOString()} stopped nudging room ${key} (cursor=${cursor}) after ${attempts} un-drained block attempts \u2014 either Codex is silently rejecting the Stop-hook output (run \`kojee-mcp doctor\` and approve Codex's trust prompt) or the room belongs to another session; the room stays pending in the ledger until its proxy drains it
|
|
76
|
+
`,
|
|
77
|
+
{ mode: 384 }
|
|
78
|
+
);
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function decideCodexStop(stopHookActive) {
|
|
83
|
+
if (stopHookActive) return "{}";
|
|
84
|
+
let pending;
|
|
85
|
+
try {
|
|
86
|
+
pending = listPending();
|
|
87
|
+
} catch {
|
|
88
|
+
return "{}";
|
|
89
|
+
}
|
|
90
|
+
if (pending.length === 0) return "{}";
|
|
91
|
+
const attempts = readNudgeAttempts();
|
|
92
|
+
const next = /* @__PURE__ */ new Map();
|
|
93
|
+
const nudgeable = [];
|
|
94
|
+
for (const room of pending) {
|
|
95
|
+
const key = room.tandemId ?? ANON_KEY;
|
|
96
|
+
const prior = attempts.get(key);
|
|
97
|
+
const count = prior !== void 0 && prior.cursor === room.cursor ? prior.count : 0;
|
|
98
|
+
if (count >= MAX_NUDGES_PER_ROOM) {
|
|
99
|
+
if (count === MAX_NUDGES_PER_ROOM) {
|
|
100
|
+
logCappedRoom(key, room.cursor, count);
|
|
101
|
+
next.set(key, { cursor: room.cursor, count: count + 1 });
|
|
102
|
+
} else {
|
|
103
|
+
next.set(key, { cursor: room.cursor, count });
|
|
104
|
+
}
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
next.set(key, { cursor: room.cursor, count: count + 1 });
|
|
108
|
+
nudgeable.push(room);
|
|
109
|
+
}
|
|
110
|
+
writeNudgeAttempts(next);
|
|
111
|
+
if (nudgeable.length === 0) return "{}";
|
|
112
|
+
return JSON.stringify({
|
|
113
|
+
decision: "block",
|
|
114
|
+
reason: CODEX_WAKE_BELL
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async function runCodexStopHook() {
|
|
118
|
+
const { stopHookActive } = await readHookStdin();
|
|
119
|
+
let out = "{}";
|
|
120
|
+
try {
|
|
121
|
+
out = decideCodexStop(stopHookActive);
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
process.stdout.write(out);
|
|
125
|
+
}
|
|
126
|
+
var CODEX_PEEK_BUDGET_MS = CODEX_PEEK_MS;
|
|
127
|
+
export {
|
|
128
|
+
CODEX_PEEK_BUDGET_MS,
|
|
129
|
+
MAX_NUDGES_PER_ROOM,
|
|
130
|
+
codexNudgeAttemptsPath,
|
|
131
|
+
codexWakeDropLogPath,
|
|
132
|
+
decideCodexStop,
|
|
133
|
+
drainedRoomsPath,
|
|
134
|
+
pendingRoomsPath,
|
|
135
|
+
runCodexStopHook
|
|
136
|
+
};
|
|
@@ -2,17 +2,17 @@ import {
|
|
|
2
2
|
DEFAULT_BROKER_URL,
|
|
3
3
|
reconcileConnectPairSlot,
|
|
4
4
|
runWizard
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XPIW4N55.js";
|
|
6
|
+
import "./chunk-6XWTUDWW.js";
|
|
6
7
|
import "./chunk-E6WMFMM2.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-77HWBSRH.js";
|
|
9
|
+
import "./chunk-TMCNB4JH.js";
|
|
9
10
|
import "./chunk-D6JKFJ6A.js";
|
|
10
11
|
import {
|
|
11
12
|
CONNECT_RUNTIMES
|
|
12
13
|
} from "./chunk-EIH2LNF4.js";
|
|
13
14
|
import "./chunk-RDLF4NQC.js";
|
|
14
15
|
import "./chunk-SQL56SEB.js";
|
|
15
|
-
import "./chunk-77HWBSRH.js";
|
|
16
16
|
import "./chunk-V5VZPYMZ.js";
|
|
17
17
|
import {
|
|
18
18
|
runPair
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "./chunk-6G6YYST6.js";
|
|
31
31
|
import "./chunk-U5HHHRXA.js";
|
|
32
32
|
import "./chunk-5DHIUN73.js";
|
|
33
|
-
import "./chunk-
|
|
33
|
+
import "./chunk-FJUAMJHU.js";
|
|
34
34
|
|
|
35
35
|
// src/wizard/connect-handler.ts
|
|
36
36
|
import os from "os";
|
|
@@ -48,6 +48,13 @@ function identityLabel(cfg) {
|
|
|
48
48
|
if (cfg?.principal_id) return cfg.principal_id;
|
|
49
49
|
return "this runtime";
|
|
50
50
|
}
|
|
51
|
+
function identityChangeWarning(runtime, oldConfig, newConfig) {
|
|
52
|
+
if (oldConfig.agent_id && newConfig?.agent_id) {
|
|
53
|
+
if (oldConfig.agent_id === newConfig.agent_id) return null;
|
|
54
|
+
return `\u26A0 WARNING: this paired ${runtime} as a NEW agent identity (${oldConfig.agent_id} \u2192 ${newConfig.agent_id}) \u2014 room seats stay with the old agent. Re-join rooms, or regenerate a code for the SAME agent in the dashboard.`;
|
|
55
|
+
}
|
|
56
|
+
return `Note: identity continuity cannot be verified for this re-connect (the broker response carries no agent id). If this code was minted for a different agent, room seats stay with the old one \u2014 re-join rooms if wakes stop.`;
|
|
57
|
+
}
|
|
51
58
|
async function runConnect(opts) {
|
|
52
59
|
if (opts.runtime === "hermes") return runConnectHermes(opts);
|
|
53
60
|
const messages = [];
|
|
@@ -89,8 +96,8 @@ async function runConnect(opts) {
|
|
|
89
96
|
const newConfig = loadPairedConfig(configPath);
|
|
90
97
|
const newLabel = identityLabel(newConfig);
|
|
91
98
|
const summary = oldConfig ? `\u21BB re-connected ${runtime} as ${newLabel} (was ${identityLabel(oldConfig)})` : `\u2713 connected ${runtime} as ${newLabel} \u2192 ${url}`;
|
|
92
|
-
const
|
|
93
|
-
Restart ${runtime} to pick up the connection
|
|
99
|
+
const warning = runtime === "codex" && oldConfig ? identityChangeWarning(runtime, oldConfig, newConfig) : null;
|
|
100
|
+
const output = [summary, ...warning ? [warning] : [], `Restart ${runtime} to pick up the connection.`].join("\n");
|
|
94
101
|
return { messages, output, exitCode: 0 };
|
|
95
102
|
}
|
|
96
103
|
async function runConnectHermes(opts) {
|
|
@@ -18,7 +18,7 @@ import "./chunk-U5HHHRXA.js";
|
|
|
18
18
|
import {
|
|
19
19
|
buildMonitorSpawn,
|
|
20
20
|
buildReplyRecipe
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-FJUAMJHU.js";
|
|
22
22
|
import {
|
|
23
23
|
deriveDiscoveryKey,
|
|
24
24
|
findClaudeAncestorPid
|
|
@@ -354,7 +354,7 @@ function formatDoctorReport(report) {
|
|
|
354
354
|
async function runDoctor() {
|
|
355
355
|
const { readRecordedRuntime } = await import("./runtime-record-OXRLTOLC.js");
|
|
356
356
|
if (readRecordedRuntime() === "codex") {
|
|
357
|
-
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-
|
|
357
|
+
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-PJFWIABF.js");
|
|
358
358
|
const report2 = collectCodexDoctorReport();
|
|
359
359
|
console.error(formatCodexDoctorReport(report2));
|
|
360
360
|
return report2.verdict === "broken" ? 1 : 0;
|