kojee-mcp 0.2.2 → 0.4.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.
@@ -0,0 +1,26 @@
1
+ import {
2
+ cleanupDiscoveryByKey,
3
+ cleanupSessionDiscovery,
4
+ discoveryFileName,
5
+ discoveryPathForKey,
6
+ readSessionDiscovery,
7
+ readSessionDiscoveryByKey,
8
+ sessionDiscoveryDir,
9
+ sessionDiscoveryPath,
10
+ sweepStaleDiscovery,
11
+ writeDiscoveryByKey,
12
+ writeSessionDiscovery
13
+ } from "./chunk-VZVGTHGF.js";
14
+ export {
15
+ cleanupDiscoveryByKey,
16
+ cleanupSessionDiscovery,
17
+ discoveryFileName,
18
+ discoveryPathForKey,
19
+ readSessionDiscovery,
20
+ readSessionDiscoveryByKey,
21
+ sessionDiscoveryDir,
22
+ sessionDiscoveryPath,
23
+ sweepStaleDiscovery,
24
+ writeDiscoveryByKey,
25
+ writeSessionDiscovery
26
+ };
@@ -0,0 +1,76 @@
1
+ import {
2
+ readHookStdin
3
+ } from "./chunk-WHTH6WBP.js";
4
+ import {
5
+ deriveDiscoveryKey,
6
+ findClaudeAncestorPid
7
+ } from "./chunk-36DMIXH7.js";
8
+ import {
9
+ readSessionDiscoveryByKey
10
+ } from "./chunk-VZVGTHGF.js";
11
+
12
+ // src/hooks/stop-hook.ts
13
+ import { spawn } from "child_process";
14
+ import fs from "fs";
15
+ async function isMonitorRunning(logPath) {
16
+ return new Promise((resolve) => {
17
+ const child = spawn("pgrep", ["-f", `tail -n \\+1 -F ${logPath}`], { stdio: "ignore" });
18
+ child.on("exit", (code) => resolve(code === 0));
19
+ child.on("error", () => resolve(false));
20
+ });
21
+ }
22
+ var STOP_POLL_TIMEOUT_MS = Number.parseInt(
23
+ process.env["KOJEE_STOP_HOOK_TIMEOUT_MS"] ?? "30000",
24
+ 10
25
+ );
26
+ async function runStopHook() {
27
+ await readHookStdin();
28
+ const ccPid = findClaudeAncestorPid();
29
+ const key = deriveDiscoveryKey(process.env["CLAUDE_PROJECT_DIR"], ccPid);
30
+ const discovery = readSessionDiscoveryByKey(key);
31
+ if (!discovery) {
32
+ process.stdout.write("{}");
33
+ return;
34
+ }
35
+ const logPath = `/tmp/kojee-events-${key}.log`;
36
+ const monitorAlive = await isMonitorRunning(logPath);
37
+ const pollTimeoutMs = monitorAlive ? 0 : STOP_POLL_TIMEOUT_MS;
38
+ let body = null;
39
+ try {
40
+ const res = await fetch(
41
+ `http://127.0.0.1:${discovery.port}/poll?type=stop&timeout_ms=${pollTimeoutMs}`
42
+ );
43
+ if (res.ok) {
44
+ body = await res.json();
45
+ }
46
+ } catch {
47
+ }
48
+ if (body && body.count > 0) {
49
+ const reason = formatEvents(body.events);
50
+ process.stdout.write(JSON.stringify({ decision: "block", reason }));
51
+ return;
52
+ }
53
+ if (!monitorAlive && fs.existsSync(logPath)) {
54
+ process.stdout.write(JSON.stringify({
55
+ decision: "block",
56
+ reason: `Before stopping, spawn the kojee Tandem event watcher: Monitor(command="tail -n +1 -F ${logPath}", persistent=true, description="kojee Tandem events"). This is needed so Tandem peer messages can wake you from idle. After spawning it once at the start of the session, you don't need to spawn it again.`
57
+ }));
58
+ return;
59
+ }
60
+ process.stdout.write("{}");
61
+ }
62
+ function formatEvents(events) {
63
+ const header = `[${events.length} unread Tandem ${events.length === 1 ? "event" : "events"}]
64
+
65
+ `;
66
+ const bodies = events.map((evt) => {
67
+ const attrs = Object.entries(evt.meta).map(([k, v]) => `${k}="${v}"`).join(" ");
68
+ return `<channel source="kojee-mcp" ${attrs}>
69
+ ${evt.content}
70
+ </channel>`;
71
+ });
72
+ return header + bodies.join("\n\n");
73
+ }
74
+ export {
75
+ runStopHook
76
+ };
@@ -0,0 +1,54 @@
1
+ import {
2
+ readHookStdin
3
+ } from "./chunk-WHTH6WBP.js";
4
+ import {
5
+ deriveDiscoveryKey,
6
+ findClaudeAncestorPid
7
+ } from "./chunk-36DMIXH7.js";
8
+ import {
9
+ readSessionDiscoveryByKey
10
+ } from "./chunk-VZVGTHGF.js";
11
+
12
+ // src/hooks/user-prompt-submit-hook.ts
13
+ async function runUserPromptSubmitHook() {
14
+ await readHookStdin();
15
+ const ccPid = findClaudeAncestorPid();
16
+ const key = deriveDiscoveryKey(process.env["CLAUDE_PROJECT_DIR"], ccPid);
17
+ const discovery = readSessionDiscoveryByKey(key);
18
+ if (!discovery) {
19
+ process.stdout.write("{}");
20
+ return;
21
+ }
22
+ let body;
23
+ try {
24
+ const res = await fetch(`http://127.0.0.1:${discovery.port}/poll?type=user-prompt-submit&timeout_ms=0`);
25
+ if (!res.ok) {
26
+ process.stdout.write("{}");
27
+ return;
28
+ }
29
+ body = await res.json();
30
+ } catch {
31
+ process.stdout.write("{}");
32
+ return;
33
+ }
34
+ if (body.count === 0) {
35
+ process.stdout.write("{}");
36
+ return;
37
+ }
38
+ process.stdout.write(JSON.stringify({ additionalContext: formatEvents(body.events) }));
39
+ }
40
+ function formatEvents(events) {
41
+ const header = `[${events.length} unread Tandem ${events.length === 1 ? "event" : "events"}]
42
+
43
+ `;
44
+ const bodies = events.map((evt) => {
45
+ const attrs = Object.entries(evt.meta).map(([k, v]) => `${k}="${v}"`).join(" ");
46
+ return `<channel source="kojee-mcp" ${attrs}>
47
+ ${evt.content}
48
+ </channel>`;
49
+ });
50
+ return header + bodies.join("\n\n");
51
+ }
52
+ export {
53
+ runUserPromptSubmitHook
54
+ };
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "kojee-mcp",
3
- "version": "0.2.2",
4
- "description": "Local MCP proxy for Kojee — handles DPoP auth, tool discovery, and governance transparently",
3
+ "version": "0.4.0",
5
4
  "type": "module",
6
5
  "main": "dist/index.js",
7
6
  "bin": {
@@ -10,29 +9,26 @@
10
9
  "scripts": {
11
10
  "build": "tsup src/cli.ts src/index.ts --format esm --dts --clean",
12
11
  "dev": "tsup src/cli.ts --format esm --watch",
13
- "typecheck": "tsc --noEmit"
12
+ "typecheck": "tsc --noEmit",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "dev:stub": "tsx dev-tools/stub-broker.ts"
14
16
  },
15
17
  "files": [
16
18
  "dist"
17
19
  ],
18
- "keywords": [
19
- "mcp",
20
- "kojee",
21
- "ai-governance",
22
- "agent-tools",
23
- "dpop"
24
- ],
25
- "license": "MIT",
26
20
  "dependencies": {
27
21
  "@modelcontextprotocol/sdk": "latest",
28
22
  "jose": "^5.0.0",
29
23
  "commander": "^12.0.0",
30
- "zod": "^3.22.0"
24
+ "zod": "^3.22.0",
25
+ "ulidx": "^2.3.0"
31
26
  },
32
27
  "devDependencies": {
33
28
  "typescript": "^5.4.0",
34
29
  "@types/node": "^20.0.0",
35
30
  "tsup": "^8.0.0",
36
- "vitest": "^1.0.0"
31
+ "vitest": "^1.0.0",
32
+ "tsx": "^4.7.0"
37
33
  }
38
34
  }