kojee-mcp 0.5.10 → 0.5.12
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/README.md +24 -0
- package/dist/ancestry-ONFBQEP5.js +8 -0
- package/dist/{codex-stop-hook-SWA53ECG.js → chunk-35XBRG3V.js} +4 -3
- package/dist/{chunk-MKDMAAMN.js → chunk-5XP2UOFK.js} +12 -0
- package/dist/{chunk-DS26OORG.js → chunk-CO73VGWM.js} +41 -23
- package/dist/chunk-FQZCENSG.js +459 -0
- package/dist/{chunk-YKS6YZKM.js → chunk-PHXO5P25.js} +1 -4
- package/dist/chunk-WLMPCX7T.js +116 -0
- package/dist/chunk-XLKGPGZT.js +0 -0
- package/dist/chunk-XXFVWP6H.js +44 -0
- package/dist/{ensure-join-7AEDJMPE.js → chunk-YKW54DKF.js} +45 -15
- package/dist/cli.js +16 -13
- package/dist/codex-stop-hook-VY7DOMAG.js +16 -0
- package/dist/{doctor-XK335W7B.js → doctor-FVTALRQD.js} +110 -15
- package/dist/ensure-join-5Y5IJ7HN.js +8 -0
- package/dist/{event-log-B27VVEMK.js → event-log-VZD7NKYX.js} +1 -1
- package/dist/event-stream-FOT7MJZH.js +19 -0
- package/dist/{gateway-client-93P1E0CZ.d.ts → gateway-client-C6yx1mfM.d.ts} +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -5
- package/dist/lib.d.ts +181 -3
- package/dist/lib.js +7 -7
- package/dist/{parent-watchdog-RZLHYP7T.js → parent-watchdog-TLU355FB.js} +1 -1
- package/dist/registry-A3VT6VJD.js +348 -0
- package/dist/server-77QRWKJM.js +14 -0
- package/dist/{stop-hook-OTCJGL6V.js → stop-hook-CUVDKXP7.js} +8 -7
- package/dist/{tail-stream-JNR4WFW3.js → tail-stream-VZ462ZON.js} +3 -2
- package/dist/{user-prompt-submit-hook-QXMC7EZU.js → user-prompt-submit-hook-PMBUPKUV.js} +6 -6
- package/dist/{webhook-sink-NWGCUDGY.js → webhook-sink-N6AUTFL3.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-SCDWPGH3.js +0 -637
- package/dist/{chunk-BJMASMKX.js → chunk-VHKPWUX7.js} +0 -0
- package/dist/{doctor-codex-SMROUYGV.js → doctor-codex-PA3WO6LR.js} +1 -1
- package/dist/{send-cli-CN5EX7PO.js → send-cli-NZP5XE7T.js} +5 -5
- package/dist/{wizard-PLGHYCT3.js → wizard-L4MYRLJI.js} +11 -11
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildCatchUpNote,
|
|
3
|
+
buildMonitorSpawn,
|
|
4
|
+
buildReplyRecipe
|
|
5
|
+
} from "./chunk-X672ZN7V.js";
|
|
6
|
+
import {
|
|
7
|
+
translateToolCallResult
|
|
8
|
+
} from "./chunk-LDZXU3DW.js";
|
|
9
|
+
|
|
10
|
+
// src/server.ts
|
|
11
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import {
|
|
14
|
+
ListToolsRequestSchema,
|
|
15
|
+
CallToolRequestSchema
|
|
16
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
+
|
|
18
|
+
// src/version.ts
|
|
19
|
+
import fs from "fs";
|
|
20
|
+
import path from "path";
|
|
21
|
+
import { fileURLToPath } from "url";
|
|
22
|
+
var FALLBACK_VERSION = "0.0.0-unknown";
|
|
23
|
+
function resolveVersion() {
|
|
24
|
+
try {
|
|
25
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const parsed = JSON.parse(
|
|
27
|
+
fs.readFileSync(path.join(here, "..", "package.json"), "utf8")
|
|
28
|
+
);
|
|
29
|
+
return typeof parsed?.version === "string" && parsed.version ? parsed.version : FALLBACK_VERSION;
|
|
30
|
+
} catch (err) {
|
|
31
|
+
process.stderr.write(
|
|
32
|
+
`kojee-mcp: could not resolve version from package.json, falling back to ${FALLBACK_VERSION}: ${String(err)}
|
|
33
|
+
`
|
|
34
|
+
);
|
|
35
|
+
return FALLBACK_VERSION;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
var VERSION = resolveVersion();
|
|
39
|
+
|
|
40
|
+
// src/server.ts
|
|
41
|
+
function buildChannelInstructions(_tandemMembershipCount, eventLogPath) {
|
|
42
|
+
const intro = `Tandem events are delivered to you in three ways depending on which is active in your Claude Code session:
|
|
43
|
+
|
|
44
|
+
(1) If channel notifications are available, you'll see them as \`<channel source="kojee-mcp" ...>\` tags in your context. Each tag carries tandem_id, message_id, from_principal, kind, severity. To respond: ${buildReplyRecipe()}.
|
|
45
|
+
|
|
46
|
+
`;
|
|
47
|
+
const monitorSection = `(2) For sustained wake-from-idle WITHOUT channel notifications, the proxy writes one event line per Tandem message to ${eventLogPath}. At the start of every session, spawn ONE Monitor task: ${buildMonitorSpawn(eventLogPath)}. Each appended line will arrive as a separate wake notification, and carries msg=<id> and cursor=<n>. To respond: ${buildReplyRecipe()}. ${buildCatchUpNote()} (\`kojee-mcp tail\` is a portable line-streamer shipped with this proxy \u2014 works on macOS, Linux, and Windows. It follows BOTH the messages log above and a status sibling; status/heartbeat telemetry never wakes you \u2014 only real messages do.)
|
|
48
|
+
|
|
49
|
+
`;
|
|
50
|
+
const listenSection = "(3) If you want to BLOCK until any single reply lands (rather than receive a stream of events), call tandem_listen(tandem_id, since=cursor, timeout_ms=N) instead.";
|
|
51
|
+
const advice = "\n\nPrefer (2) at session start \u2014 it's the default no-allowlist wake mechanism. (1) supplements it when channels are enabled; (3) is for one-shot blocking waits.";
|
|
52
|
+
return intro + monitorSection + listenSection + advice;
|
|
53
|
+
}
|
|
54
|
+
function tandemIdArg(args) {
|
|
55
|
+
return typeof args["tandem_id"] === "string" ? args["tandem_id"] : null;
|
|
56
|
+
}
|
|
57
|
+
async function executeToolCall(registry, name, args, hooks) {
|
|
58
|
+
const rawResult = await registry.callTool(name, args);
|
|
59
|
+
const result = translateToolCallResult(rawResult);
|
|
60
|
+
if (!result.isError) {
|
|
61
|
+
if (name === "tandem_join") {
|
|
62
|
+
try {
|
|
63
|
+
hooks?.onTandemJoin?.(tandemIdArg(args));
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error("[mcp] onTandemJoin hook failed:", err?.message ?? String(err));
|
|
66
|
+
}
|
|
67
|
+
} else if (name === "tandem_leave") {
|
|
68
|
+
try {
|
|
69
|
+
hooks?.onTandemLeave?.(tandemIdArg(args));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error("[mcp] onTandemLeave hook failed:", err?.message ?? String(err));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
function createMcpServer(registry, adapter, tandemMembershipCount = -1, eventLogPath, hooks) {
|
|
78
|
+
const capabilities = { tools: {} };
|
|
79
|
+
if (adapter.supportsChannels) {
|
|
80
|
+
capabilities.experimental = { "claude/channel": {} };
|
|
81
|
+
}
|
|
82
|
+
const server = new Server(
|
|
83
|
+
{ name: "kojee-mcp", version: VERSION },
|
|
84
|
+
{
|
|
85
|
+
capabilities,
|
|
86
|
+
...adapter.supportsChannels ? { instructions: buildChannelInstructions(tandemMembershipCount, eventLogPath ?? "") } : {}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
90
|
+
const tools = registry.getAllTools().map((t) => ({
|
|
91
|
+
name: t.name,
|
|
92
|
+
description: t.description,
|
|
93
|
+
inputSchema: t.inputSchema
|
|
94
|
+
}));
|
|
95
|
+
return { tools };
|
|
96
|
+
});
|
|
97
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
98
|
+
const { name, arguments: args } = request.params;
|
|
99
|
+
const result = await executeToolCall(registry, name, args ?? {}, hooks);
|
|
100
|
+
return { content: result.content, isError: result.isError };
|
|
101
|
+
});
|
|
102
|
+
return server;
|
|
103
|
+
}
|
|
104
|
+
async function startMcpServer(server) {
|
|
105
|
+
const transport = new StdioServerTransport();
|
|
106
|
+
await server.connect(transport);
|
|
107
|
+
console.error("[mcp] Server started on stdio transport");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
VERSION,
|
|
112
|
+
buildChannelInstructions,
|
|
113
|
+
executeToolCall,
|
|
114
|
+
createMcpServer,
|
|
115
|
+
startMcpServer
|
|
116
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildReplyRecipe
|
|
3
|
+
} from "./chunk-X672ZN7V.js";
|
|
4
|
+
|
|
5
|
+
// src/adapters/claude-code.ts
|
|
6
|
+
function computeSeverity(event) {
|
|
7
|
+
if (event.type === "state_change") return "high";
|
|
8
|
+
if (event.mentions && event.mentions.length > 0) {
|
|
9
|
+
return "high";
|
|
10
|
+
}
|
|
11
|
+
return "normal";
|
|
12
|
+
}
|
|
13
|
+
function formatBody(event) {
|
|
14
|
+
if (event.type === "state_change") {
|
|
15
|
+
return `[Tandem: ${event.tandem_id}] ${event.from.displayname}: ${event.content.body}`;
|
|
16
|
+
}
|
|
17
|
+
return [
|
|
18
|
+
`[Tandem: ${event.tandem_id}] ${event.from.displayname} (${event.from.principal}) \u2014 ${event.kind}:`,
|
|
19
|
+
event.content.body,
|
|
20
|
+
"",
|
|
21
|
+
`> ${buildReplyRecipe({ tandem_id: event.tandem_id, message_id: event.id })}`
|
|
22
|
+
].join("\n");
|
|
23
|
+
}
|
|
24
|
+
var claudeCodeAdapter = {
|
|
25
|
+
runtime: "claude-code",
|
|
26
|
+
supportsChannels: true,
|
|
27
|
+
formatTandemEvent(event) {
|
|
28
|
+
const meta = {
|
|
29
|
+
tandem_id: event.tandem_id,
|
|
30
|
+
message_id: event.id,
|
|
31
|
+
cursor: String(event.cursor),
|
|
32
|
+
kind: event.kind,
|
|
33
|
+
from_principal: event.from.principal,
|
|
34
|
+
from_display: event.from.displayname,
|
|
35
|
+
severity: computeSeverity(event)
|
|
36
|
+
};
|
|
37
|
+
if (event.wake_reason) meta.wake_reason = event.wake_reason;
|
|
38
|
+
return { content: formatBody(event), meta };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
claudeCodeAdapter
|
|
44
|
+
};
|
|
@@ -3,8 +3,9 @@ var OBJECT_ID_RE = /^[0-9a-f]{24}$/i;
|
|
|
3
3
|
var DEFAULT_PER_CALL_TIMEOUT_MS = 1e4;
|
|
4
4
|
function parseTandemsConfig(raw) {
|
|
5
5
|
const trimmed = (raw ?? "").trim();
|
|
6
|
-
if (trimmed.length === 0) return { mode: "auto", ids: [], invalid: [] };
|
|
6
|
+
if (trimmed.length === 0) return { mode: "auto-local", ids: [], invalid: [] };
|
|
7
7
|
if (trimmed.toLowerCase() === "none") return { mode: "disabled", ids: [], invalid: [] };
|
|
8
|
+
if (trimmed.toLowerCase() === "auto-agent") return { mode: "auto-agent", ids: [], invalid: [] };
|
|
8
9
|
const ids = [];
|
|
9
10
|
const invalid = [];
|
|
10
11
|
for (const entry of trimmed.split(/[\s,]+/)) {
|
|
@@ -25,26 +26,54 @@ async function ensureJoinTandems(opts) {
|
|
|
25
26
|
for (const bad of config.invalid) {
|
|
26
27
|
log(`[ensure-join] skipping invalid tandem id "${bad}" (not a 24-hex ObjectId)`);
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
if (config.mode === "explicit") {
|
|
30
|
-
ids = config.ids;
|
|
31
|
-
log(`[ensure-join] mode=explicit n=${ids.length} (KOJEE_TANDEMS)`);
|
|
32
|
-
} else {
|
|
33
|
-
let listed = null;
|
|
29
|
+
const listAgentTandems = async () => {
|
|
34
30
|
try {
|
|
35
|
-
|
|
31
|
+
return opts.listTandems ? await opts.listTandems() : null;
|
|
36
32
|
} catch (err) {
|
|
37
33
|
log(`[ensure-join] tandem_list threw: ${err.message}`);
|
|
38
|
-
|
|
34
|
+
return null;
|
|
39
35
|
}
|
|
36
|
+
};
|
|
37
|
+
const warnListFailed = () => {
|
|
38
|
+
log(
|
|
39
|
+
"[ensure-join] tandem_list failed \u2014 cannot re-seat this session (set KOJEE_TANDEMS=<ids> to pin, or KOJEE_TANDEMS=none to silence)"
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
let ids;
|
|
43
|
+
if (config.mode === "explicit") {
|
|
44
|
+
ids = config.ids;
|
|
45
|
+
log(`[ensure-join] mode=explicit n=${ids.length} (KOJEE_TANDEMS)`);
|
|
46
|
+
} else if (config.mode === "auto-agent") {
|
|
47
|
+
const listed = await listAgentTandems();
|
|
40
48
|
if (listed === null) {
|
|
41
|
-
|
|
42
|
-
"[ensure-join] tandem_list failed \u2014 cannot auto re-seat this session (set KOJEE_TANDEMS=<ids> to pin, or KOJEE_TANDEMS=none to silence)"
|
|
43
|
-
);
|
|
49
|
+
warnListFailed();
|
|
44
50
|
return result;
|
|
45
51
|
}
|
|
46
52
|
ids = listed;
|
|
47
|
-
log(`[ensure-join] mode=auto n=${ids.length} (
|
|
53
|
+
log(`[ensure-join] mode=auto-agent n=${ids.length} (legacy \u2014 re-seating every room this agent holds a seat)`);
|
|
54
|
+
} else {
|
|
55
|
+
const mem = opts.roomMemory;
|
|
56
|
+
if (mem && mem.hasMemory()) {
|
|
57
|
+
ids = mem.read().filter((id) => OBJECT_ID_RE.test(id));
|
|
58
|
+
log(`[ensure-join] mode=auto-local n=${ids.length} (rejoining this session's own rooms from local memory)`);
|
|
59
|
+
} else {
|
|
60
|
+
const listed = await listAgentTandems();
|
|
61
|
+
if (listed === null) {
|
|
62
|
+
warnListFailed();
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
ids = listed;
|
|
66
|
+
if (mem) {
|
|
67
|
+
try {
|
|
68
|
+
mem.seed(ids);
|
|
69
|
+
log(`[ensure-join] mode=auto-local n=${ids.length} (first run \u2014 seeded local memory from membership)`);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
log(`[ensure-join] mode=auto-local n=${ids.length} (first run \u2014 seed write FAILED: ${err.message}; joining without persisting)`);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
log(`[ensure-join] mode=auto-local n=${ids.length} (no room-memory port \u2014 falling back to agent-scoped list)`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
48
77
|
}
|
|
49
78
|
for (const id of ids) {
|
|
50
79
|
const outcome = await joinOne(opts.gateway, id, perCallTimeoutMs);
|
|
@@ -90,7 +119,8 @@ async function joinOne(gateway, tandemId, perCallTimeoutMs) {
|
|
|
90
119
|
clearTimeout(timer);
|
|
91
120
|
}
|
|
92
121
|
}
|
|
122
|
+
|
|
93
123
|
export {
|
|
94
|
-
|
|
95
|
-
|
|
124
|
+
parseTandemsConfig,
|
|
125
|
+
ensureJoinTandems
|
|
96
126
|
};
|
package/dist/cli.js
CHANGED
|
@@ -3,24 +3,27 @@ import {
|
|
|
3
3
|
runPair
|
|
4
4
|
} from "./chunk-OGHDTFAX.js";
|
|
5
5
|
import {
|
|
6
|
-
VERSION,
|
|
7
6
|
startProxy
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-BJMASMKX.js";
|
|
7
|
+
} from "./chunk-FQZCENSG.js";
|
|
8
|
+
import "./chunk-XXFVWP6H.js";
|
|
11
9
|
import {
|
|
12
10
|
pairedConfigPath
|
|
13
11
|
} from "./chunk-YH27B6SW.js";
|
|
14
|
-
import "./chunk-JXMVZEQ7.js";
|
|
15
12
|
import "./chunk-HSR3GXCL.js";
|
|
16
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-JXMVZEQ7.js";
|
|
17
14
|
import "./chunk-2MIISF2W.js";
|
|
18
15
|
import {
|
|
19
16
|
defaultPairedKeystorePath,
|
|
20
17
|
deriveKeystorePath
|
|
21
18
|
} from "./chunk-CH32ELFX.js";
|
|
22
19
|
import "./chunk-BLEGIR35.js";
|
|
20
|
+
import {
|
|
21
|
+
VERSION
|
|
22
|
+
} from "./chunk-WLMPCX7T.js";
|
|
23
|
+
import "./chunk-X672ZN7V.js";
|
|
23
24
|
import "./chunk-LDZXU3DW.js";
|
|
25
|
+
import "./chunk-VHKPWUX7.js";
|
|
26
|
+
import "./chunk-YKW54DKF.js";
|
|
24
27
|
|
|
25
28
|
// src/cli.ts
|
|
26
29
|
import { Command } from "commander";
|
|
@@ -42,15 +45,15 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
42
45
|
});
|
|
43
46
|
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) => {
|
|
44
47
|
if (opts.type === "stop") {
|
|
45
|
-
const { runStopHook } = await import("./stop-hook-
|
|
48
|
+
const { runStopHook } = await import("./stop-hook-CUVDKXP7.js");
|
|
46
49
|
await runStopHook();
|
|
47
50
|
process.exit(0);
|
|
48
51
|
} else if (opts.type === "user-prompt-submit") {
|
|
49
|
-
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-
|
|
52
|
+
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-PMBUPKUV.js");
|
|
50
53
|
await runUserPromptSubmitHook();
|
|
51
54
|
process.exit(0);
|
|
52
55
|
} else if (opts.type === "codex-stop") {
|
|
53
|
-
const { runCodexStopHook } = await import("./codex-stop-hook-
|
|
56
|
+
const { runCodexStopHook } = await import("./codex-stop-hook-VY7DOMAG.js");
|
|
54
57
|
await runCodexStopHook();
|
|
55
58
|
process.exit(0);
|
|
56
59
|
} else {
|
|
@@ -76,7 +79,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
76
79
|
program.command("send <tandem_id>").description(
|
|
77
80
|
"Send a Tandem message using this machine's paired credentials (~/.kojee). Prints one JSON envelope to stdout: {ok, message_id, cursor, text} on success, {ok:false, error:<typed code>, message} on failure (exit 1)."
|
|
78
81
|
).requiredOption("--body <text>", "Message body (required)").option("--reply-to <message_id>", "Message id this send replies to").option("--kind <kind>", "Message kind: message | status (default: backend default)").action(async (tandemId, opts) => {
|
|
79
|
-
const { runSendCli } = await import("./send-cli-
|
|
82
|
+
const { runSendCli } = await import("./send-cli-NZP5XE7T.js");
|
|
80
83
|
const { exitCode, envelope } = await runSendCli({
|
|
81
84
|
tandemId,
|
|
82
85
|
body: opts.body,
|
|
@@ -87,7 +90,7 @@ program.command("send <tandem_id>").description(
|
|
|
87
90
|
process.exit(exitCode);
|
|
88
91
|
});
|
|
89
92
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
90
|
-
const { runTail } = await import("./tail-stream-
|
|
93
|
+
const { runTail } = await import("./tail-stream-VZ462ZON.js");
|
|
91
94
|
try {
|
|
92
95
|
await runTail(filePath);
|
|
93
96
|
} catch (err) {
|
|
@@ -96,7 +99,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
96
99
|
}
|
|
97
100
|
});
|
|
98
101
|
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 () => {
|
|
99
|
-
const { runDoctor } = await import("./doctor-
|
|
102
|
+
const { runDoctor } = await import("./doctor-FVTALRQD.js");
|
|
100
103
|
const code = await runDoctor();
|
|
101
104
|
process.exit(code);
|
|
102
105
|
});
|
|
@@ -137,7 +140,7 @@ program.command("init").description(
|
|
|
137
140
|
console.error("Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run `init` \u2014 or pass --token/--pair-code, or run `init` in a terminal for the guided wizard.");
|
|
138
141
|
process.exit(1);
|
|
139
142
|
}
|
|
140
|
-
const { runWizard } = await import("./wizard-
|
|
143
|
+
const { runWizard } = await import("./wizard-L4MYRLJI.js");
|
|
141
144
|
const result = await runWizard({
|
|
142
145
|
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
|
143
146
|
...opts.uninstall ? { uninstall: true } : {},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODEX_PEEK_BUDGET_MS,
|
|
3
|
+
codexPendingMarkerPath,
|
|
4
|
+
decideCodexStopHook,
|
|
5
|
+
defaultPeekPending,
|
|
6
|
+
runCodexStopHook
|
|
7
|
+
} from "./chunk-35XBRG3V.js";
|
|
8
|
+
import "./chunk-LSUB6QMP.js";
|
|
9
|
+
import "./chunk-X672ZN7V.js";
|
|
10
|
+
export {
|
|
11
|
+
CODEX_PEEK_BUDGET_MS,
|
|
12
|
+
codexPendingMarkerPath,
|
|
13
|
+
decideCodexStopHook,
|
|
14
|
+
defaultPeekPending,
|
|
15
|
+
runCodexStopHook
|
|
16
|
+
};
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import "./chunk-XLKGPGZT.js";
|
|
2
|
+
import {
|
|
3
|
+
loadControlToken
|
|
4
|
+
} from "./chunk-GI2CKKBL.js";
|
|
1
5
|
import {
|
|
2
6
|
monitorHeartbeatPath,
|
|
3
7
|
statusLogPath
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CO73VGWM.js";
|
|
5
9
|
import {
|
|
6
10
|
discoveryPathForKey,
|
|
7
|
-
readSessionDiscoveryByKey
|
|
11
|
+
readSessionDiscoveryByKey,
|
|
12
|
+
sessionDiscoveryDir
|
|
8
13
|
} from "./chunk-DO42NPNR.js";
|
|
9
14
|
import {
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
15
|
+
loadPairedConfig
|
|
16
|
+
} from "./chunk-YH27B6SW.js";
|
|
17
|
+
import "./chunk-BLEGIR35.js";
|
|
12
18
|
import {
|
|
13
19
|
buildMonitorSpawn,
|
|
14
20
|
buildReplyRecipe
|
|
@@ -16,14 +22,74 @@ import {
|
|
|
16
22
|
import {
|
|
17
23
|
deriveDiscoveryKey,
|
|
18
24
|
findClaudeAncestorPid
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import {
|
|
21
|
-
loadPairedConfig
|
|
22
|
-
} from "./chunk-YH27B6SW.js";
|
|
23
|
-
import "./chunk-BLEGIR35.js";
|
|
25
|
+
} from "./chunk-VHKPWUX7.js";
|
|
24
26
|
|
|
25
27
|
// src/doctor.ts
|
|
26
28
|
import fs from "fs";
|
|
29
|
+
import os from "os";
|
|
30
|
+
import path from "path";
|
|
31
|
+
var SENTINEL_FRESH_MS = 5e3;
|
|
32
|
+
function eventLogKey(logPath) {
|
|
33
|
+
const m = logPath.replace(/^.*[/\\]/, "").match(/^kojee-events-(.+)\.log$/);
|
|
34
|
+
return m ? m[1] : null;
|
|
35
|
+
}
|
|
36
|
+
function defaultListSiblingProxies(selfKey) {
|
|
37
|
+
const dir = sessionDiscoveryDir();
|
|
38
|
+
let names;
|
|
39
|
+
try {
|
|
40
|
+
names = fs.readdirSync(dir);
|
|
41
|
+
} catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const name of names) {
|
|
46
|
+
if (!name.startsWith("cc-") || !name.endsWith(".json")) continue;
|
|
47
|
+
const key = name.slice("cc-".length, -".json".length);
|
|
48
|
+
if (key === selfKey) continue;
|
|
49
|
+
try {
|
|
50
|
+
const entry = JSON.parse(fs.readFileSync(path.join(dir, name), "utf8"));
|
|
51
|
+
const proxyPid = entry.proxyPid ?? entry.pid;
|
|
52
|
+
const proxyPidAlive = typeof proxyPid === "number" && defaultIsPidAlive(proxyPid);
|
|
53
|
+
let webhookStatusLine = null;
|
|
54
|
+
if (entry.eventLogPath) {
|
|
55
|
+
const last = defaultReadLastStatusLine(statusLogPath(entry.eventLogPath));
|
|
56
|
+
if (last && last.includes("status=webhook enabled")) webhookStatusLine = last;
|
|
57
|
+
}
|
|
58
|
+
out.push({ key, proxyPidAlive, webhookStatusLine });
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
async function resolveThisWebhookEnabled() {
|
|
65
|
+
try {
|
|
66
|
+
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
67
|
+
return resolveWebhookConfig().enabled === true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function defaultListMonitorSentinels() {
|
|
73
|
+
const dir = os.tmpdir();
|
|
74
|
+
let names;
|
|
75
|
+
try {
|
|
76
|
+
names = fs.readdirSync(dir);
|
|
77
|
+
} catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const name of names) {
|
|
82
|
+
const m = name.match(/^kojee-monitor-(.+)\.alive$/);
|
|
83
|
+
if (!m) continue;
|
|
84
|
+
const p = path.join(dir, name);
|
|
85
|
+
try {
|
|
86
|
+
const s = fs.statSync(p);
|
|
87
|
+
out.push({ path: p, key: m[1], mtimeMs: s.mtimeMs });
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
27
93
|
function defaultIsPidAlive(pid) {
|
|
28
94
|
try {
|
|
29
95
|
process.kill(pid, 0);
|
|
@@ -33,23 +99,23 @@ function defaultIsPidAlive(pid) {
|
|
|
33
99
|
return false;
|
|
34
100
|
}
|
|
35
101
|
}
|
|
36
|
-
function defaultStat(
|
|
102
|
+
function defaultStat(path2) {
|
|
37
103
|
try {
|
|
38
|
-
const s = fs.statSync(
|
|
104
|
+
const s = fs.statSync(path2);
|
|
39
105
|
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
40
106
|
} catch {
|
|
41
107
|
return null;
|
|
42
108
|
}
|
|
43
109
|
}
|
|
44
110
|
var STATUS_TAIL_BYTES = 64 * 1024;
|
|
45
|
-
function defaultReadLastStatusLine(
|
|
111
|
+
function defaultReadLastStatusLine(path2) {
|
|
46
112
|
let fd = null;
|
|
47
113
|
try {
|
|
48
|
-
const { size } = fs.statSync(
|
|
114
|
+
const { size } = fs.statSync(path2);
|
|
49
115
|
const start = Math.max(0, size - STATUS_TAIL_BYTES);
|
|
50
116
|
const len = size - start;
|
|
51
117
|
if (len === 0) return null;
|
|
52
|
-
fd = fs.openSync(
|
|
118
|
+
fd = fs.openSync(path2, "r");
|
|
53
119
|
const buf = Buffer.alloc(len);
|
|
54
120
|
fs.readSync(fd, buf, 0, len, start);
|
|
55
121
|
const text = buf.toString("utf8");
|
|
@@ -181,6 +247,35 @@ async function collectDoctorReport(deps = {}) {
|
|
|
181
247
|
detail: sentinelFresh ? "live (a Monitor is reading the log)" : "no fresh sentinel \u2014 spawn the Monitor (recipe below)"
|
|
182
248
|
});
|
|
183
249
|
}
|
|
250
|
+
const webhookSinkEnabled = deps.webhookSinkEnabled !== void 0 ? deps.webhookSinkEnabled : await resolveThisWebhookEnabled();
|
|
251
|
+
if (!webhookSinkEnabled) {
|
|
252
|
+
const listSiblings = deps.listSiblingProxies ?? defaultListSiblingProxies;
|
|
253
|
+
const siblingsWithWebhook = listSiblings(discoveryKey).filter(
|
|
254
|
+
(s) => s.proxyPidAlive && s.webhookStatusLine
|
|
255
|
+
);
|
|
256
|
+
if (siblingsWithWebhook.length > 0) {
|
|
257
|
+
checks.push({
|
|
258
|
+
name: "sibling webhook sink",
|
|
259
|
+
ok: "warn",
|
|
260
|
+
detail: `another live proxy has a webhook sink enabled while this session's is OFF \u2014 Tandem events may be delivered via that sibling's webhook, not your Monitor. Sibling key(s): ${siblingsWithWebhook.map((s) => s.key).join(", ")}.`
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (logPath) {
|
|
265
|
+
const currentKey = eventLogKey(logPath);
|
|
266
|
+
const listSentinels = deps.listMonitorSentinels ?? defaultListMonitorSentinels;
|
|
267
|
+
const now = Date.now();
|
|
268
|
+
const staleLiveMonitors = listSentinels().filter(
|
|
269
|
+
(s) => now - s.mtimeMs < SENTINEL_FRESH_MS && s.key !== currentKey
|
|
270
|
+
);
|
|
271
|
+
if (staleLiveMonitors.length > 0) {
|
|
272
|
+
checks.push({
|
|
273
|
+
name: "stale Monitor",
|
|
274
|
+
ok: "warn",
|
|
275
|
+
detail: `a live Monitor is tailing ${staleLiveMonitors.map((s) => s.path).join(", ")}, but current events go to ${logPath} \u2014 re-spawn the Monitor with the recipe below.`
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
184
279
|
checks.push({
|
|
185
280
|
name: "wire contract",
|
|
186
281
|
ok: "unknown",
|
|
@@ -233,7 +328,7 @@ function formatDoctorReport(report) {
|
|
|
233
328
|
async function runDoctor() {
|
|
234
329
|
const { readRecordedRuntime } = await import("./runtime-record-WO4IECM6.js");
|
|
235
330
|
if (readRecordedRuntime() === "codex") {
|
|
236
|
-
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-
|
|
331
|
+
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-PA3WO6LR.js");
|
|
237
332
|
const report2 = collectCodexDoctorReport();
|
|
238
333
|
console.error(formatCodexDoctorReport(report2));
|
|
239
334
|
return report2.verdict === "broken" ? 1 : 0;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
3
|
+
createAdaptiveWatchdog,
|
|
4
|
+
createBackoffController,
|
|
5
|
+
normalizeBackendEvent,
|
|
6
|
+
sanitizeDisplayname,
|
|
7
|
+
serializeCursorMap,
|
|
8
|
+
startEventStream
|
|
9
|
+
} from "./chunk-5XP2UOFK.js";
|
|
10
|
+
import "./chunk-2MIISF2W.js";
|
|
11
|
+
export {
|
|
12
|
+
UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
13
|
+
createAdaptiveWatchdog,
|
|
14
|
+
createBackoffController,
|
|
15
|
+
normalizeBackendEvent,
|
|
16
|
+
sanitizeDisplayname,
|
|
17
|
+
serializeCursorMap,
|
|
18
|
+
startEventStream
|
|
19
|
+
};
|
|
@@ -26,6 +26,11 @@ interface LoadedKeyPair {
|
|
|
26
26
|
publicJwk: JWK;
|
|
27
27
|
kid: string;
|
|
28
28
|
}
|
|
29
|
+
interface McpToolDefinition {
|
|
30
|
+
name: string;
|
|
31
|
+
description: string;
|
|
32
|
+
inputSchema: Record<string, unknown>;
|
|
33
|
+
}
|
|
29
34
|
interface GovernanceMeta {
|
|
30
35
|
decision: "require_approval" | "deny" | "allow";
|
|
31
36
|
approval_id?: string;
|
|
@@ -89,4 +94,4 @@ declare class GatewayClient {
|
|
|
89
94
|
private tryParseErrorBody;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
export { GatewayClient as G, type KeystoreData as K, type LoadedKeyPair as L, type ProxyConfig as P, type ToolCallResult as T };
|
|
97
|
+
export { GatewayClient as G, type KeystoreData as K, type LoadedKeyPair as L, type McpToolDefinition as M, type ProxyConfig as P, type ToolCallResult as T };
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTandemIds,
|
|
3
3
|
startProxy
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-BJMASMKX.js";
|
|
7
|
-
import "./chunk-JXMVZEQ7.js";
|
|
4
|
+
} from "./chunk-FQZCENSG.js";
|
|
5
|
+
import "./chunk-XXFVWP6H.js";
|
|
8
6
|
import "./chunk-HSR3GXCL.js";
|
|
9
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-JXMVZEQ7.js";
|
|
10
8
|
import "./chunk-2MIISF2W.js";
|
|
11
9
|
import "./chunk-CH32ELFX.js";
|
|
12
10
|
import "./chunk-BLEGIR35.js";
|
|
11
|
+
import "./chunk-WLMPCX7T.js";
|
|
12
|
+
import "./chunk-X672ZN7V.js";
|
|
13
13
|
import "./chunk-LDZXU3DW.js";
|
|
14
|
+
import "./chunk-VHKPWUX7.js";
|
|
15
|
+
import "./chunk-YKW54DKF.js";
|
|
14
16
|
export {
|
|
15
17
|
listTandemIds,
|
|
16
18
|
startProxy
|