kojee-mcp 0.5.0 → 0.5.2

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.
Files changed (33) hide show
  1. package/README.md +81 -4
  2. package/dist/{chunk-VLZADEFC.js → chunk-2TUAFAIW.js} +6 -9
  3. package/dist/chunk-BLEGIR35.js +43 -0
  4. package/dist/chunk-C6GZ2L2W.js +38 -0
  5. package/dist/{chunk-W6YRLSD4.js → chunk-DO42NPNR.js} +9 -16
  6. package/dist/chunk-EW72ZNQL.js +39 -0
  7. package/dist/chunk-F7L25L2J.js +60 -0
  8. package/dist/chunk-LVL25VLO.js +22 -0
  9. package/dist/chunk-SQL56SEB.js +14 -0
  10. package/dist/{chunk-QB22PD6T.js → chunk-WBMX4CHB.js} +29 -9
  11. package/dist/{chunk-LCFCCWMM.js → chunk-YEC7IHIG.js} +136 -78
  12. package/dist/{chunk-GBOTBYEP.js → chunk-YH27B6SW.js} +7 -8
  13. package/dist/chunk-ZW4SW7LJ.js +225 -0
  14. package/dist/cli.js +54 -80
  15. package/dist/codex-stop-hook-JOTBCS5K.js +72 -0
  16. package/dist/{doctor-GILTOH2R.js → doctor-TSHOMT5X.js} +29 -14
  17. package/dist/doctor-codex-BMI5JOO6.js +130 -0
  18. package/dist/{event-log-R6VW6GAF.js → event-log-RSTM4PLL.js} +3 -2
  19. package/dist/index.d.ts +9 -0
  20. package/dist/index.js +4 -3
  21. package/dist/{install-D2HIPOMT.js → install-WBIUVBZW.js} +5 -4
  22. package/dist/{paired-config-RB4SABOS.js → paired-config-JTFLHMZ2.js} +2 -1
  23. package/dist/runtime-record-WO4IECM6.js +14 -0
  24. package/dist/runtimes-CO43XUUK.js +12 -0
  25. package/dist/{session-discovery-QE5TTAPS.js → session-discovery-FNMJGFPM.js} +2 -1
  26. package/dist/{stop-hook-VLQS6QPR.js → stop-hook-SEPWWETV.js} +6 -5
  27. package/dist/{tail-stream-UZ42UIWO.js → tail-stream-BYKO4DW6.js} +4 -3
  28. package/dist/{user-prompt-submit-hook-C42DPDBO.js → user-prompt-submit-hook-ARPEO6FF.js} +2 -1
  29. package/dist/webhook-config-5TLLX7RA.js +10 -0
  30. package/dist/webhook-sink-7OYZBWXA.js +163 -0
  31. package/dist/wizard-7KHD5JT4.js +265 -0
  32. package/package.json +1 -1
  33. package/dist/chunk-E26AHU6J.js +0 -27
package/dist/cli.js CHANGED
@@ -1,17 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ loadPairedConfig,
4
+ pairedConfigPath,
5
+ savePairedConfig
6
+ } from "./chunk-YH27B6SW.js";
2
7
  import {
3
8
  AuthModule,
4
9
  VERSION,
5
10
  startProxy
6
- } from "./chunk-LCFCCWMM.js";
7
- import "./chunk-QB22PD6T.js";
8
- import "./chunk-E26AHU6J.js";
11
+ } from "./chunk-YEC7IHIG.js";
9
12
  import "./chunk-BJMASMKX.js";
10
- import {
11
- loadPairedConfig,
12
- pairedConfigPath,
13
- savePairedConfig
14
- } from "./chunk-GBOTBYEP.js";
13
+ import "./chunk-C6GZ2L2W.js";
14
+ import "./chunk-WBMX4CHB.js";
15
+ import "./chunk-BLEGIR35.js";
15
16
 
16
17
  // src/cli.ts
17
18
  import { Command } from "commander";
@@ -82,22 +83,26 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
82
83
  process.exit(1);
83
84
  }
84
85
  });
85
- program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption("--type <type>", "Hook type: stop or user-prompt-submit").action(async (opts) => {
86
+ program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption("--type <type>", "Hook type: stop, user-prompt-submit, or codex-stop").action(async (opts) => {
86
87
  if (opts.type === "stop") {
87
- const { runStopHook } = await import("./stop-hook-VLQS6QPR.js");
88
+ const { runStopHook } = await import("./stop-hook-SEPWWETV.js");
88
89
  await runStopHook();
89
90
  process.exit(0);
90
91
  } else if (opts.type === "user-prompt-submit") {
91
- const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-C42DPDBO.js");
92
+ const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-ARPEO6FF.js");
92
93
  await runUserPromptSubmitHook();
93
94
  process.exit(0);
95
+ } else if (opts.type === "codex-stop") {
96
+ const { runCodexStopHook } = await import("./codex-stop-hook-JOTBCS5K.js");
97
+ await runCodexStopHook();
98
+ process.exit(0);
94
99
  } else {
95
- console.error(`Unknown hook type: ${opts.type}. Expected 'stop' or 'user-prompt-submit'.`);
100
+ console.error(`Unknown hook type: ${opts.type}. Expected 'stop', 'user-prompt-submit', or 'codex-stop'.`);
96
101
  process.exit(1);
97
102
  }
98
103
  });
99
104
  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) => {
100
- const { installHooks, uninstallHooks } = await import("./install-D2HIPOMT.js");
105
+ const { installHooks, uninstallHooks } = await import("./install-WBIUVBZW.js");
101
106
  if (opts.uninstall) {
102
107
  const removed = uninstallHooks({ hooksPath: opts.hooksPath });
103
108
  console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
@@ -112,7 +117,7 @@ Restart Claude Code for hooks to take effect.`
112
117
  }
113
118
  });
114
119
  program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
115
- const { runTail } = await import("./tail-stream-UZ42UIWO.js");
120
+ const { runTail } = await import("./tail-stream-BYKO4DW6.js");
116
121
  try {
117
122
  await runTail(filePath);
118
123
  } catch (err) {
@@ -121,79 +126,47 @@ program.command("tail <path>").description("Stream a file's contents and follow
121
126
  }
122
127
  });
123
128
  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 () => {
124
- const { runDoctor } = await import("./doctor-GILTOH2R.js");
129
+ const { runDoctor } = await import("./doctor-TSHOMT5X.js");
125
130
  const code = await runDoctor();
126
131
  process.exit(code);
127
132
  });
128
- program.command("init").description("Install kojee into Claude Code (MCP server entry + hooks). Run after `kojee-mcp pair`.").option("--config-path <path>", "Override default ~/.claude.json").option("--uninstall", "Remove the kojee MCP server entry and hooks").action(async (opts) => {
129
- const { loadPairedConfig: loadPairedConfig2 } = await import("./paired-config-RB4SABOS.js");
133
+ program.command("init").description(
134
+ "Set up kojee for a runtime (claude-code | hermes | openclaw | codex). Interactive when stdin is a TTY; `--runtime <id>` for non-interactive/CI. Run after `kojee-mcp pair`."
135
+ ).option("--runtime <id>", "Target runtime: claude-code | hermes | openclaw | codex").option("--config-path <path>", "Override the runtime's MCP-config path").option("--hooks-path <path>", "Override the runtime's hooks-file path").option("--webhook-url <url>", "Webhook receiver URL (codex/hermes/openclaw)").option("--webhook-secret <secret>", "Webhook HMAC secret (generated if omitted)").option("--uninstall", "Remove the kojee config for the chosen (or recorded) runtime").action(async (opts) => {
136
+ const { loadPairedConfig: loadPairedConfig2 } = await import("./paired-config-JTFLHMZ2.js");
130
137
  if (loadPairedConfig2() === null && !opts.uninstall) {
131
138
  console.error("Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run `init`.");
132
139
  process.exit(1);
133
140
  }
134
- const { runInit, runUninstall } = await import("./install-D2HIPOMT.js");
135
- if (opts.uninstall) {
136
- const report = runUninstall({ configPath: opts.configPath });
137
- console.error(formatUninstall(report));
138
- } else {
139
- const report = runInit({ configPath: opts.configPath });
140
- console.error(formatInit(report));
141
- }
141
+ const { runWizard } = await import("./wizard-7KHD5JT4.js");
142
+ const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
143
+ const result = await runWizard({
144
+ ...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
145
+ ...opts.uninstall ? { uninstall: true } : {},
146
+ ...opts.configPath ? { configPath: opts.configPath } : {},
147
+ ...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
148
+ ...opts.webhookUrl ? { webhookUrl: opts.webhookUrl } : {},
149
+ ...opts.webhookSecret ? { webhookSecret: opts.webhookSecret } : {},
150
+ interactive,
151
+ ...interactive ? { promptRuntime: promptRuntimeFromTty } : {}
152
+ });
153
+ console.error(result.output);
154
+ process.exit(result.exitCode);
142
155
  });
143
- function formatInit(report) {
144
- const tick = (s) => {
145
- if (s === "added") return "\u2713 added";
146
- if (s === "already-installed") return "\u21BB already installed";
147
- if (s === "preserved-different") return "\u26A0 preserved (existing entry differs \u2014 left untouched)";
148
- if (s === "not-found") return "\u2014 not found";
149
- return s ?? "\u2014";
150
- };
151
- const lines = ["Installing kojee for Claude:"];
152
- for (const t of report.targets) {
153
- const label = t.kind === "cli" ? "CLI" : "Claude.app";
154
- lines.push("");
155
- lines.push(` ${t.path} (${label})`);
156
- lines.push(` mcpServers.kojee ${tick(t.mcpServer)}`);
157
- if (t.kind === "cli") {
158
- if (t.hooksPath) {
159
- lines.push(` ${t.hooksPath} (hooks)`);
160
- }
161
- lines.push(` hooks.Stop ${tick(t.stopHook)}`);
162
- lines.push(` hooks.UserPromptSubmit ${tick(t.userPromptSubmitHook)}`);
163
- } else {
164
- lines.push(` (hooks not applicable for Claude.app agent mode)`);
165
- }
166
- }
167
- lines.push("");
168
- const desktopWritten = report.targets.some((t) => t.kind === "desktop" && t.mcpServer === "added");
169
- if (desktopWritten) {
170
- lines.push(
171
- "Existing Claude.app agent-mode sessions snapshotted the previous config",
172
- "and won't pick up this change automatically. Start a NEW agent-mode",
173
- "session (not a resumed one) to use the updated kojee config.",
174
- ""
175
- );
176
- }
177
- lines.push("To verify: in any new CC session, run /mcp and confirm `kojee` is listed.");
178
- lines.push(" run /hooks and confirm both Stop and UserPromptSubmit show the kojee entries.");
179
- lines.push("To remove: `kojee-mcp init --uninstall`");
180
- return lines.join("\n");
181
- }
182
- function formatUninstall(report) {
183
- const lines = ["Removing kojee from Claude:"];
184
- for (const t of report.targets) {
185
- const label = t.kind === "cli" ? "CLI" : "Claude.app";
186
- lines.push("");
187
- lines.push(` ${t.path} (${label})`);
188
- lines.push(` mcpServers.kojee ${t.mcpServer ? "\u2713 removed" : "\u2014 not found"}`);
189
- if (t.kind === "cli") {
190
- if (t.hooksPath) {
191
- lines.push(` ${t.hooksPath} (hooks)`);
192
- }
193
- lines.push(` hook entries ${t.hooks ? "\u2713 removed" : "\u2014 not found"}`);
194
- }
156
+ async function promptRuntimeFromTty() {
157
+ const { RUNTIME_MENU } = await import("./runtimes-CO43XUUK.js");
158
+ const readline = await import("readline/promises");
159
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
160
+ try {
161
+ const menu = RUNTIME_MENU.map((m) => `[${m.index}] ${m.runtime}`).join(" ");
162
+ const answer = (await rl.question(`Which runtime is this proxy for? ${menu}
163
+ > `)).trim();
164
+ const byIndex = RUNTIME_MENU.find((m) => String(m.index) === answer);
165
+ if (byIndex) return byIndex.runtime;
166
+ return answer;
167
+ } finally {
168
+ rl.close();
195
169
  }
196
- return lines.join("\n");
197
170
  }
198
171
  program.option("--token <token>", "Gateway token (for token-mode)").option("--url <url>", "Broker base URL (for token-mode; required if --token is passed)").option(
199
172
  "--keystore-path <path>",
@@ -202,6 +175,7 @@ program.option("--token <token>", "Gateway token (for token-mode)").option("--ur
202
175
  let token = opts.token;
203
176
  let url = opts.url;
204
177
  let keystorePath = opts.keystorePath;
178
+ const authMode = token ? "token" : "paired";
205
179
  if (token) {
206
180
  if (!url) {
207
181
  console.error("--url is required when --token is provided");
@@ -210,7 +184,7 @@ program.option("--token <token>", "Gateway token (for token-mode)").option("--ur
210
184
  url = url.replace(/\/+$/, "");
211
185
  keystorePath ??= deriveKeystorePath(token);
212
186
  } else {
213
- const { loadPairedConfig: loadPairedConfig2 } = await import("./paired-config-RB4SABOS.js");
187
+ const { loadPairedConfig: loadPairedConfig2 } = await import("./paired-config-JTFLHMZ2.js");
214
188
  const cfg = loadPairedConfig2();
215
189
  if (!cfg) {
216
190
  console.error(
@@ -223,7 +197,7 @@ program.option("--token <token>", "Gateway token (for token-mode)").option("--ur
223
197
  keystorePath ??= defaultPairedKeystorePath();
224
198
  }
225
199
  try {
226
- await startProxy({ token, url, keystorePath });
200
+ await startProxy({ token, url, keystorePath, authMode });
227
201
  } catch (err) {
228
202
  console.error("[kojee-mcp] Fatal error:", err);
229
203
  process.exit(1);
@@ -0,0 +1,72 @@
1
+ import {
2
+ readHookStdin
3
+ } from "./chunk-LSUB6QMP.js";
4
+ import {
5
+ buildCodexWakeReason
6
+ } from "./chunk-C6GZ2L2W.js";
7
+
8
+ // src/hooks/codex-stop-hook.ts
9
+ import fs from "fs";
10
+ import os from "os";
11
+ import path from "path";
12
+ var CODEX_PEEK_MS = clampPeekMs(
13
+ Number.parseInt(process.env["KOJEE_CODEX_PEEK_MS"] ?? "150", 10)
14
+ );
15
+ var MARKER_STALE_MS = 3e4;
16
+ function clampPeekMs(raw) {
17
+ if (!Number.isFinite(raw) || raw <= 0) return 150;
18
+ return Math.min(raw, 500);
19
+ }
20
+ function decideCodexStopHook(deps) {
21
+ if (deps.stopHookActive) return "{}";
22
+ const peek = deps.peekPending();
23
+ if (!peek.pending || peek.cursor === null) return "{}";
24
+ return JSON.stringify({
25
+ decision: "block",
26
+ reason: buildCodexWakeReason(peek.cursor)
27
+ });
28
+ }
29
+ function codexPendingMarkerPath(sessionId) {
30
+ const safe = (sessionId ?? "no-session").replace(/[^A-Za-z0-9._-]/g, "_");
31
+ return path.join(os.homedir(), ".kojee", `codex-pending-${safe}`);
32
+ }
33
+ function defaultPeekPending(sessionId) {
34
+ const markerPath = codexPendingMarkerPath(sessionId);
35
+ try {
36
+ const st = fs.statSync(markerPath);
37
+ const ageMs = Date.now() - st.mtimeMs;
38
+ if (ageMs > MARKER_STALE_MS) return { pending: false, cursor: null };
39
+ let cursor = 0;
40
+ try {
41
+ const body = fs.readFileSync(markerPath, "utf8").trim().split(/\s+/)[0];
42
+ const n = Number.parseInt(body ?? "", 10);
43
+ if (Number.isFinite(n) && n >= 0) cursor = n;
44
+ } catch {
45
+ }
46
+ return { pending: true, cursor };
47
+ } catch {
48
+ return { pending: false, cursor: null };
49
+ }
50
+ }
51
+ async function runCodexStopHook() {
52
+ const { sessionId, stopHookActive } = await readHookStdin();
53
+ const out = decideCodexStopHook({
54
+ stopHookActive,
55
+ peekPending: () => {
56
+ try {
57
+ return defaultPeekPending(sessionId);
58
+ } catch {
59
+ return { pending: false, cursor: null };
60
+ }
61
+ }
62
+ });
63
+ process.stdout.write(out);
64
+ }
65
+ var CODEX_PEEK_BUDGET_MS = CODEX_PEEK_MS;
66
+ export {
67
+ CODEX_PEEK_BUDGET_MS,
68
+ codexPendingMarkerPath,
69
+ decideCodexStopHook,
70
+ defaultPeekPending,
71
+ runCodexStopHook
72
+ };
@@ -1,22 +1,23 @@
1
1
  import {
2
- buildMonitorSpawn,
3
- buildReplyRecipe
4
- } from "./chunk-E26AHU6J.js";
2
+ loadPairedConfig
3
+ } from "./chunk-YH27B6SW.js";
5
4
  import {
6
5
  deriveDiscoveryKey,
7
6
  findClaudeAncestorPid
8
7
  } from "./chunk-BJMASMKX.js";
8
+ import {
9
+ buildMonitorSpawn,
10
+ buildReplyRecipe
11
+ } from "./chunk-C6GZ2L2W.js";
9
12
  import {
10
13
  monitorHeartbeatPath,
11
14
  statusLogPath
12
- } from "./chunk-VLZADEFC.js";
15
+ } from "./chunk-2TUAFAIW.js";
13
16
  import {
14
17
  discoveryPathForKey,
15
18
  readSessionDiscoveryByKey
16
- } from "./chunk-W6YRLSD4.js";
17
- import {
18
- loadPairedConfig
19
- } from "./chunk-GBOTBYEP.js";
19
+ } from "./chunk-DO42NPNR.js";
20
+ import "./chunk-BLEGIR35.js";
20
21
 
21
22
  // src/doctor.ts
22
23
  import fs from "fs";
@@ -73,13 +74,20 @@ async function collectDoctorReport(deps = {}) {
73
74
  const discoveryKey = deriveDiscoveryKey(projectDir, ccPid);
74
75
  const discoveryPath = discoveryPathForKey(discoveryKey);
75
76
  const checks = [];
76
- const paired = deps.pairedConfigPresent !== void 0 ? deps.pairedConfigPresent : loadPairedConfig() !== null;
77
- checks.push({
78
- name: "paired config",
79
- ok: paired,
80
- detail: paired ? "present" : "MISSING \u2014 run `kojee-mcp pair <code> --url <broker>`"
81
- });
82
77
  const discovery = readDiscovery(discoveryKey);
78
+ const tokenMode = discovery?.authMode === "token";
79
+ const paired = deps.pairedConfigPresent !== void 0 ? deps.pairedConfigPresent : loadPairedConfig() !== null;
80
+ checks.push(
81
+ tokenMode ? {
82
+ name: "paired config",
83
+ ok: true,
84
+ detail: "n/a (token mode) \u2014 proxy launched with --token; no ~/.kojee/config.json by design"
85
+ } : {
86
+ name: "paired config",
87
+ ok: paired,
88
+ detail: paired ? "present" : "MISSING \u2014 run `kojee-mcp pair <code> --url <broker>`"
89
+ }
90
+ );
83
91
  let logPath = null;
84
92
  if (!discovery) {
85
93
  checks.push({
@@ -210,6 +218,13 @@ function formatDoctorReport(report) {
210
218
  return lines.join("\n");
211
219
  }
212
220
  async function runDoctor() {
221
+ const { readRecordedRuntime } = await import("./runtime-record-WO4IECM6.js");
222
+ if (readRecordedRuntime() === "codex") {
223
+ const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-BMI5JOO6.js");
224
+ const report2 = collectCodexDoctorReport();
225
+ console.error(formatCodexDoctorReport(report2));
226
+ return report2.verdict === "broken" ? 1 : 0;
227
+ }
213
228
  const report = await collectDoctorReport();
214
229
  console.error(formatDoctorReport(report));
215
230
  return report.verdict === "broken" ? 1 : 0;
@@ -0,0 +1,130 @@
1
+ import {
2
+ defaultCodexConfigPath,
3
+ defaultCodexHooksPath
4
+ } from "./chunk-ZW4SW7LJ.js";
5
+ import "./chunk-SQL56SEB.js";
6
+ import {
7
+ CODEX_LISTEN_CAP_MS
8
+ } from "./chunk-C6GZ2L2W.js";
9
+ import "./chunk-BLEGIR35.js";
10
+ import {
11
+ resolveWebhookConfig
12
+ } from "./chunk-F7L25L2J.js";
13
+
14
+ // src/doctor-codex.ts
15
+ import fs from "fs";
16
+ function parseCodexEnvFromToml(toml) {
17
+ const env = {};
18
+ let inEnv = false;
19
+ for (const line of toml.split("\n")) {
20
+ const header = line.trim();
21
+ const isTableHeader = /^\[\[?[^\]]+\]\]?$/.test(header);
22
+ if (isTableHeader) {
23
+ inEnv = header === "[mcp_servers.kojee.env]";
24
+ continue;
25
+ }
26
+ if (!inEnv) continue;
27
+ const eq = line.indexOf("=");
28
+ if (eq <= 0) continue;
29
+ const key = line.slice(0, eq).trim();
30
+ if (!/^[A-Za-z0-9_.-]+$/.test(key)) continue;
31
+ let value = line.slice(eq + 1).trim();
32
+ const quote = value[0];
33
+ if ((quote === '"' || quote === "'") && value.endsWith(quote)) {
34
+ value = value.slice(1, -1);
35
+ if (quote === '"') value = value.replace(/\\"/g, '"').replace(/\\\\/g, "\\");
36
+ }
37
+ env[key] = value;
38
+ }
39
+ return env;
40
+ }
41
+ var WIZARD_RERUN = "re-run `kojee-mcp init --runtime codex`";
42
+ function defaultReadFile(path) {
43
+ return () => {
44
+ try {
45
+ return fs.readFileSync(path, "utf8");
46
+ } catch {
47
+ return null;
48
+ }
49
+ };
50
+ }
51
+ function collectCodexDoctorReport(deps = {}) {
52
+ const readConfigToml = deps.readConfigToml ?? defaultReadFile(defaultCodexConfigPath());
53
+ const readHooksJson = deps.readHooksJson ?? defaultReadFile(defaultCodexHooksPath());
54
+ const checks = [];
55
+ const toml = readConfigToml() ?? "";
56
+ const env = deps.env ?? parseCodexEnvFromToml(toml);
57
+ const hasKojeeTable = toml.includes("[mcp_servers.kojee]");
58
+ const hasRuntimeEnv = /KOJEE_RUNTIME\s*=\s*"codex"/.test(toml);
59
+ const configOk = hasKojeeTable && hasRuntimeEnv;
60
+ checks.push({
61
+ name: "~/.codex/config.toml [mcp_servers.kojee]",
62
+ ok: configOk,
63
+ detail: configOk ? 'present with env.KOJEE_RUNTIME="codex" (Tier-1 contract)' : `MISSING [mcp_servers.kojee] or env.KOJEE_RUNTIME="codex" \u2014 ${WIZARD_RERUN}`
64
+ });
65
+ const hooksRaw = readHooksJson() ?? "{}";
66
+ let hookPresent = false;
67
+ try {
68
+ const hooks = JSON.parse(hooksRaw);
69
+ hookPresent = !!hooks.hooks?.Stop?.some(
70
+ (e) => e.hooks?.some((h) => (h.command ?? "").includes("hook --type=codex-stop"))
71
+ );
72
+ } catch {
73
+ }
74
+ if (!hookPresent && /hook --type=codex-stop/.test(toml)) hookPresent = true;
75
+ checks.push({
76
+ name: "Codex Stop hook (codex-stop)",
77
+ ok: hookPresent,
78
+ detail: hookPresent ? "present (fast PEEK + model-chosen bounded listen)" : `MISSING in ~/.codex/hooks.json / [[hooks.Stop]] \u2014 ${WIZARD_RERUN}`
79
+ });
80
+ const resolution = resolveWebhookConfig(env);
81
+ if (resolution.enabled && resolution.config) {
82
+ checks.push({
83
+ name: "webhook sink",
84
+ ok: true,
85
+ detail: `enabled \u2014 ${resolution.config.redactedSummary}`
86
+ });
87
+ } else if (resolution.error) {
88
+ checks.push({
89
+ name: "webhook sink",
90
+ ok: false,
91
+ detail: `${resolution.error} \u2014 ${WIZARD_RERUN} (it generates a secret)`
92
+ });
93
+ } else {
94
+ checks.push({
95
+ name: "webhook sink",
96
+ ok: false,
97
+ detail: `not configured (no KOJEE_WEBHOOK_URL) \u2014 ${WIZARD_RERUN}`
98
+ });
99
+ }
100
+ if (deps.pingReceiver) {
101
+ const reachable = deps.pingReceiver();
102
+ checks.push({
103
+ name: "webhook receiver (optional ping)",
104
+ ok: reachable ? true : "warn",
105
+ detail: reachable ? "reachable" : "unreachable \u2014 stand up your receiver at KOJEE_WEBHOOK_URL (owner-built; see doctor note)"
106
+ });
107
+ }
108
+ const verdict = checks.some((c) => c.ok === false) ? "broken" : checks.some((c) => c.ok === "warn") ? "degraded" : "healthy";
109
+ return { checks, verdict };
110
+ }
111
+ function formatCodexDoctorReport(report) {
112
+ const mark = (ok) => ok === true ? "\u2713" : ok === "warn" ? "\u26A0" : ok === "unknown" ? "?" : "\u2717";
113
+ const lines = [];
114
+ lines.push(`kojee-mcp doctor (codex) \u2014 verdict: ${report.verdict.toUpperCase()}`);
115
+ lines.push("");
116
+ lines.push(" Wake mode: webhook-sink + stop-hook peek (Codex has no channel injection).");
117
+ lines.push(` Bounded-listen cap: ${CODEX_LISTEN_CAP_MS}ms (model picks listen vs drain vs ignore).`);
118
+ lines.push("");
119
+ for (const c of report.checks) {
120
+ lines.push(` ${mark(c.ok)} ${c.name}: ${c.detail}`);
121
+ }
122
+ lines.push("");
123
+ lines.push("NOTE: live Codex verification (hook fires, MCP connects, bounded listen) is an owner step.");
124
+ return lines.join("\n");
125
+ }
126
+ export {
127
+ collectCodexDoctorReport,
128
+ formatCodexDoctorReport,
129
+ parseCodexEnvFromToml
130
+ };
@@ -5,8 +5,9 @@ import {
5
5
  startEventLog,
6
6
  statusLogPath,
7
7
  sweepStaleEventLogs
8
- } from "./chunk-VLZADEFC.js";
9
- import "./chunk-W6YRLSD4.js";
8
+ } from "./chunk-2TUAFAIW.js";
9
+ import "./chunk-DO42NPNR.js";
10
+ import "./chunk-BLEGIR35.js";
10
11
  export {
11
12
  STATUS_LINE_PREFIX,
12
13
  monitorHeartbeatPath,
package/dist/index.d.ts CHANGED
@@ -2,6 +2,15 @@ interface ProxyConfig {
2
2
  token: string;
3
3
  url: string;
4
4
  keystorePath: string;
5
+ /**
6
+ * How credentials were resolved at launch: "token" when `--token` was passed
7
+ * on the CLI, "paired" when read from ~/.kojee/config.json. The proxy records
8
+ * this in its session-discovery file so `kojee-mcp doctor` can render the
9
+ * pairing check honestly on a token-mode box (no config.json by design).
10
+ * Defaults to "paired" when unset (back-compat with callers predating the
11
+ * field).
12
+ */
13
+ authMode?: "token" | "paired";
5
14
  }
6
15
 
7
16
  declare function startProxy(config: ProxyConfig): Promise<void>;
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  startProxy
3
- } from "./chunk-LCFCCWMM.js";
4
- import "./chunk-QB22PD6T.js";
5
- import "./chunk-E26AHU6J.js";
3
+ } from "./chunk-YEC7IHIG.js";
6
4
  import "./chunk-BJMASMKX.js";
5
+ import "./chunk-C6GZ2L2W.js";
6
+ import "./chunk-WBMX4CHB.js";
7
+ import "./chunk-BLEGIR35.js";
7
8
  export {
8
9
  startProxy
9
10
  };
@@ -1,3 +1,7 @@
1
+ import {
2
+ secureFile
3
+ } from "./chunk-BLEGIR35.js";
4
+
1
5
  // src/hooks/install.ts
2
6
  import fs from "fs";
3
7
  import os from "os";
@@ -48,10 +52,7 @@ function writeConfig(p, cfg) {
48
52
  const dir = path.dirname(p);
49
53
  fs.mkdirSync(dir, { recursive: true });
50
54
  fs.writeFileSync(p, JSON.stringify(cfg, null, 2), { mode: 384 });
51
- try {
52
- fs.chmodSync(p, 384);
53
- } catch {
54
- }
55
+ secureFile(p);
55
56
  }
56
57
  function hasKojeeEntry(arr, command) {
57
58
  if (!arr) return false;
@@ -2,7 +2,8 @@ import {
2
2
  loadPairedConfig,
3
3
  pairedConfigPath,
4
4
  savePairedConfig
5
- } from "./chunk-GBOTBYEP.js";
5
+ } from "./chunk-YH27B6SW.js";
6
+ import "./chunk-BLEGIR35.js";
6
7
  export {
7
8
  loadPairedConfig,
8
9
  pairedConfigPath,
@@ -0,0 +1,14 @@
1
+ import {
2
+ clearRuntimeRecord,
3
+ readRecordedRuntime,
4
+ recordRuntime,
5
+ runtimeRecordPath
6
+ } from "./chunk-EW72ZNQL.js";
7
+ import "./chunk-SQL56SEB.js";
8
+ import "./chunk-BLEGIR35.js";
9
+ export {
10
+ clearRuntimeRecord,
11
+ readRecordedRuntime,
12
+ recordRuntime,
13
+ runtimeRecordPath
14
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ RUNTIME_MENU,
3
+ WEBHOOK_RUNTIMES,
4
+ WIZARD_RUNTIMES,
5
+ isWizardRuntime
6
+ } from "./chunk-LVL25VLO.js";
7
+ export {
8
+ RUNTIME_MENU,
9
+ WEBHOOK_RUNTIMES,
10
+ WIZARD_RUNTIMES,
11
+ isWizardRuntime
12
+ };
@@ -10,7 +10,8 @@ import {
10
10
  sweepStaleDiscovery,
11
11
  writeDiscoveryByKey,
12
12
  writeSessionDiscovery
13
- } from "./chunk-W6YRLSD4.js";
13
+ } from "./chunk-DO42NPNR.js";
14
+ import "./chunk-BLEGIR35.js";
14
15
  export {
15
16
  cleanupDiscoveryByKey,
16
17
  cleanupSessionDiscovery,
@@ -1,20 +1,21 @@
1
1
  import {
2
2
  readHookStdin
3
3
  } from "./chunk-LSUB6QMP.js";
4
- import {
5
- buildMonitorNudge
6
- } from "./chunk-E26AHU6J.js";
7
4
  import {
8
5
  deriveDiscoveryKey,
9
6
  findClaudeAncestorPid
10
7
  } from "./chunk-BJMASMKX.js";
8
+ import {
9
+ buildMonitorNudge
10
+ } from "./chunk-C6GZ2L2W.js";
11
11
  import {
12
12
  monitorHeartbeatPath,
13
13
  nudgeSentinelPath
14
- } from "./chunk-VLZADEFC.js";
14
+ } from "./chunk-2TUAFAIW.js";
15
15
  import {
16
16
  readSessionDiscoveryByKey
17
- } from "./chunk-W6YRLSD4.js";
17
+ } from "./chunk-DO42NPNR.js";
18
+ import "./chunk-BLEGIR35.js";
18
19
 
19
20
  // src/hooks/stop-hook.ts
20
21
  import fs from "fs";
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  createAdaptiveWatchdog
3
- } from "./chunk-QB22PD6T.js";
3
+ } from "./chunk-WBMX4CHB.js";
4
4
  import {
5
5
  STATUS_LINE_PREFIX,
6
6
  monitorHeartbeatPath,
7
7
  statusLogPath
8
- } from "./chunk-VLZADEFC.js";
9
- import "./chunk-W6YRLSD4.js";
8
+ } from "./chunk-2TUAFAIW.js";
9
+ import "./chunk-DO42NPNR.js";
10
+ import "./chunk-BLEGIR35.js";
10
11
 
11
12
  // src/tail-stream.ts
12
13
  import fs from "fs";
@@ -7,7 +7,8 @@ import {
7
7
  } from "./chunk-BJMASMKX.js";
8
8
  import {
9
9
  readSessionDiscoveryByKey
10
- } from "./chunk-W6YRLSD4.js";
10
+ } from "./chunk-DO42NPNR.js";
11
+ import "./chunk-BLEGIR35.js";
11
12
 
12
13
  // src/hooks/user-prompt-submit-hook.ts
13
14
  async function runUserPromptSubmitHook() {
@@ -0,0 +1,10 @@
1
+ import {
2
+ WEBHOOK_DEFAULT_MAX_RETRIES,
3
+ WEBHOOK_DEFAULT_TIMEOUT_MS,
4
+ resolveWebhookConfig
5
+ } from "./chunk-F7L25L2J.js";
6
+ export {
7
+ WEBHOOK_DEFAULT_MAX_RETRIES,
8
+ WEBHOOK_DEFAULT_TIMEOUT_MS,
9
+ resolveWebhookConfig
10
+ };