kojee-mcp 0.5.6 → 0.5.7
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-2BDAM3TH.js → chunk-GATXJ6UT.js} +63 -32
- package/dist/cli.js +3 -3
- package/dist/index.js +1 -1
- package/dist/parent-watchdog-RZLHYP7T.js +65 -0
- package/dist/{reconnect-scheduler-JSXCJKQP.js → reconnect-scheduler-ARV6JIWK.js} +18 -8
- package/package.json +1 -1
- package/dist/{stop-hook-TRAMQYNE.js → stop-hook-46BJD55B.js} +3 -3
- package/dist/{wizard-OSOAY4GO.js → wizard-UOXQYJLP.js} +4 -4
|
@@ -317,11 +317,42 @@ async function startProxy(config) {
|
|
|
317
317
|
`[kojee-mcp] Ready \u2014 ${registry.toolCount} tools available from ${config.url}`
|
|
318
318
|
);
|
|
319
319
|
let activeStreamHandle = null;
|
|
320
|
-
const { createJoinReconnectScheduler } = await import("./reconnect-scheduler-
|
|
320
|
+
const { createJoinReconnectScheduler } = await import("./reconnect-scheduler-ARV6JIWK.js");
|
|
321
321
|
const joinReconnect = createJoinReconnectScheduler({
|
|
322
|
-
|
|
322
|
+
// BOOT-RACE (Bug B): report whether the stream handle was actually ready.
|
|
323
|
+
// `false` ⇒ activeStreamHandle is still null (tandem_join fired before the
|
|
324
|
+
// stream was set up) → the scheduler queues the reconnect and flushes it on
|
|
325
|
+
// notifyReady() once the handle is assigned (see below), instead of silently
|
|
326
|
+
// dropping it as the old `activeStreamHandle?.reconnect()` no-op did.
|
|
327
|
+
reconnect: () => {
|
|
328
|
+
if (!activeStreamHandle) return false;
|
|
329
|
+
activeStreamHandle.reconnect();
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
323
332
|
});
|
|
324
333
|
const onTandemJoin = () => joinReconnect.requestReconnect();
|
|
334
|
+
const teardownSteps = [];
|
|
335
|
+
let shuttingDown = false;
|
|
336
|
+
function shutdown(reason) {
|
|
337
|
+
if (shuttingDown) return;
|
|
338
|
+
shuttingDown = true;
|
|
339
|
+
activeStreamHandle?.();
|
|
340
|
+
for (const step of teardownSteps) {
|
|
341
|
+
try {
|
|
342
|
+
const maybe = step();
|
|
343
|
+
if (maybe && typeof maybe.catch === "function") {
|
|
344
|
+
maybe.catch((err) => {
|
|
345
|
+
console.error("[kojee-mcp] async shutdown step failed:", err?.message ?? err);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
} catch (err) {
|
|
349
|
+
console.error("[kojee-mcp] shutdown step failed:", err?.message ?? err);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
console.error(`[kojee-mcp] shutting down (${reason}), exiting`);
|
|
353
|
+
process.exit(0);
|
|
354
|
+
}
|
|
355
|
+
const ccPid = await findClaudeAncestorPid();
|
|
325
356
|
const { ensureJoinTandems } = await import("./ensure-join-7AEDJMPE.js");
|
|
326
357
|
await ensureJoinTandems({
|
|
327
358
|
gateway,
|
|
@@ -352,7 +383,6 @@ async function startProxy(config) {
|
|
|
352
383
|
const { createWebhookSink } = await import("./webhook-sink-NWGCUDGY.js");
|
|
353
384
|
sweepStaleDiscovery();
|
|
354
385
|
sweepStaleEventLogs();
|
|
355
|
-
const ccPid = await findClaudeAncestorPid();
|
|
356
386
|
const projectDir = process.env["CLAUDE_PROJECT_DIR"];
|
|
357
387
|
const discoveryKey = deriveDiscoveryKey(projectDir, ccPid);
|
|
358
388
|
const eventLog = startEventLog({ key: discoveryKey });
|
|
@@ -436,16 +466,16 @@ async function startProxy(config) {
|
|
|
436
466
|
authMode: config.authMode ?? "paired"
|
|
437
467
|
});
|
|
438
468
|
const cleanupDiscoveryFile = () => cleanupDiscoveryByKey(discoveryKey);
|
|
439
|
-
process.on("exit", () =>
|
|
440
|
-
process.on("SIGINT", () => {
|
|
469
|
+
process.on("exit", () => {
|
|
441
470
|
cleanupDiscoveryFile();
|
|
442
|
-
|
|
471
|
+
eventLog.cleanup();
|
|
443
472
|
});
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
process.exit(0);
|
|
473
|
+
teardownSteps.push(() => {
|
|
474
|
+
void webhookSink?.stop();
|
|
447
475
|
});
|
|
448
|
-
|
|
476
|
+
teardownSteps.push(() => cleanupDiscoveryFile());
|
|
477
|
+
teardownSteps.push(() => eventLog.cleanup());
|
|
478
|
+
teardownSteps.push(() => hookServer.stop());
|
|
449
479
|
streamHandle = await startEventStream({
|
|
450
480
|
brokerUrl: config.url,
|
|
451
481
|
token: config.token,
|
|
@@ -482,24 +512,13 @@ async function startProxy(config) {
|
|
|
482
512
|
})()
|
|
483
513
|
});
|
|
484
514
|
activeStreamHandle = streamHandle;
|
|
485
|
-
|
|
486
|
-
process.stdin.on("end", () => {
|
|
487
|
-
cancelStream();
|
|
488
|
-
void webhookSink?.stop();
|
|
489
|
-
cleanupDiscoveryFile();
|
|
490
|
-
eventLog.cleanup();
|
|
491
|
-
hookServer.stop().finally(() => {
|
|
492
|
-
console.error("[kojee-mcp] stdin closed, exiting");
|
|
493
|
-
process.exit(0);
|
|
494
|
-
});
|
|
495
|
-
});
|
|
515
|
+
joinReconnect.notifyReady();
|
|
496
516
|
} else if (needsWebhookEventStream()) {
|
|
497
517
|
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-RSTM4PLL.js");
|
|
498
518
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
499
519
|
const { createWebhookSink } = await import("./webhook-sink-NWGCUDGY.js");
|
|
500
520
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
501
521
|
sweepStaleEventLogs();
|
|
502
|
-
const ccPid = await findClaudeAncestorPid();
|
|
503
522
|
const projectDir = process.env["CLAUDE_PROJECT_DIR"];
|
|
504
523
|
const discoveryKey = deriveDiscoveryKey(projectDir, ccPid);
|
|
505
524
|
const eventLog = startEventLog({ key: discoveryKey });
|
|
@@ -529,6 +548,10 @@ async function startProxy(config) {
|
|
|
529
548
|
onTandemJoin
|
|
530
549
|
});
|
|
531
550
|
process.on("exit", () => eventLog.cleanup());
|
|
551
|
+
teardownSteps.push(() => {
|
|
552
|
+
void webhookSink?.stop();
|
|
553
|
+
});
|
|
554
|
+
teardownSteps.push(() => eventLog.cleanup());
|
|
532
555
|
const streamHandle = await startEventStream({
|
|
533
556
|
brokerUrl: config.url,
|
|
534
557
|
token: config.token,
|
|
@@ -550,19 +573,27 @@ async function startProxy(config) {
|
|
|
550
573
|
})()
|
|
551
574
|
});
|
|
552
575
|
activeStreamHandle = streamHandle;
|
|
553
|
-
|
|
554
|
-
streamHandle();
|
|
555
|
-
void webhookSink?.stop();
|
|
556
|
-
eventLog.cleanup();
|
|
557
|
-
console.error("[kojee-mcp] stdin closed, exiting");
|
|
558
|
-
process.exit(0);
|
|
559
|
-
});
|
|
576
|
+
joinReconnect.notifyReady();
|
|
560
577
|
} else {
|
|
561
578
|
server = createMcpServer(registry, adapter, tandemMembershipCount);
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
579
|
+
}
|
|
580
|
+
process.stdin.on("end", () => shutdown("stdin end"));
|
|
581
|
+
process.stdin.on("close", () => shutdown("stdin close"));
|
|
582
|
+
process.on("SIGHUP", () => shutdown("SIGHUP"));
|
|
583
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
584
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
585
|
+
if (ccPid !== null) {
|
|
586
|
+
const { createParentWatchdog } = await import("./parent-watchdog-RZLHYP7T.js");
|
|
587
|
+
const watchdog = createParentWatchdog({
|
|
588
|
+
ccPid,
|
|
589
|
+
onParentGone: () => shutdown("parent (Claude Code) gone")
|
|
565
590
|
});
|
|
591
|
+
watchdog.start();
|
|
592
|
+
teardownSteps.push(() => watchdog.stop());
|
|
593
|
+
} else {
|
|
594
|
+
console.error(
|
|
595
|
+
"[kojee-mcp] no Claude Code ancestor found \u2014 parent-liveness watchdog NOT armed (stdin/signal handlers still cover clean exits)"
|
|
596
|
+
);
|
|
566
597
|
}
|
|
567
598
|
await startMcpServer(server);
|
|
568
599
|
}
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
VERSION,
|
|
4
4
|
startProxy
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GATXJ6UT.js";
|
|
6
6
|
import "./chunk-X672ZN7V.js";
|
|
7
7
|
import "./chunk-BJMASMKX.js";
|
|
8
8
|
import {
|
|
@@ -83,7 +83,7 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
83
83
|
});
|
|
84
84
|
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) => {
|
|
85
85
|
if (opts.type === "stop") {
|
|
86
|
-
const { runStopHook } = await import("./stop-hook-
|
|
86
|
+
const { runStopHook } = await import("./stop-hook-46BJD55B.js");
|
|
87
87
|
await runStopHook();
|
|
88
88
|
process.exit(0);
|
|
89
89
|
} else if (opts.type === "user-prompt-submit") {
|
|
@@ -158,7 +158,7 @@ program.command("init").description(
|
|
|
158
158
|
console.error("Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run `init`.");
|
|
159
159
|
process.exit(1);
|
|
160
160
|
}
|
|
161
|
-
const { runWizard } = await import("./wizard-
|
|
161
|
+
const { runWizard } = await import("./wizard-UOXQYJLP.js");
|
|
162
162
|
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
163
163
|
const result = await runWizard({
|
|
164
164
|
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findClaudeAncestorPid
|
|
3
|
+
} from "./chunk-BJMASMKX.js";
|
|
4
|
+
|
|
5
|
+
// src/runtime/parent-watchdog.ts
|
|
6
|
+
function defaultIsPidAlive(pid) {
|
|
7
|
+
try {
|
|
8
|
+
process.kill(pid, 0);
|
|
9
|
+
return true;
|
|
10
|
+
} catch (err) {
|
|
11
|
+
if (err.code === "EPERM") return true;
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function createParentWatchdog(opts) {
|
|
16
|
+
const intervalMs = opts.intervalMs ?? 7e3;
|
|
17
|
+
const isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
18
|
+
const resolveAncestor = opts.resolveAncestor ?? (() => findClaudeAncestorPid());
|
|
19
|
+
let timer = null;
|
|
20
|
+
let fired = false;
|
|
21
|
+
let checking = false;
|
|
22
|
+
function stop() {
|
|
23
|
+
if (timer !== null) {
|
|
24
|
+
clearInterval(timer);
|
|
25
|
+
timer = null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function check() {
|
|
29
|
+
if (fired || checking) return;
|
|
30
|
+
checking = true;
|
|
31
|
+
try {
|
|
32
|
+
const ccPidAlive = isPidAlive(opts.ccPid);
|
|
33
|
+
if (ccPidAlive) return;
|
|
34
|
+
let ancestor = null;
|
|
35
|
+
try {
|
|
36
|
+
ancestor = await resolveAncestor();
|
|
37
|
+
} catch {
|
|
38
|
+
ancestor = null;
|
|
39
|
+
}
|
|
40
|
+
if (ancestor !== null) return;
|
|
41
|
+
fired = true;
|
|
42
|
+
stop();
|
|
43
|
+
try {
|
|
44
|
+
opts.onParentGone();
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
} finally {
|
|
48
|
+
checking = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
start() {
|
|
53
|
+
if (timer !== null || fired) return;
|
|
54
|
+
timer = setInterval(() => {
|
|
55
|
+
void check();
|
|
56
|
+
}, intervalMs);
|
|
57
|
+
timer.unref?.();
|
|
58
|
+
},
|
|
59
|
+
stop
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
createParentWatchdog,
|
|
64
|
+
defaultIsPidAlive
|
|
65
|
+
};
|
|
@@ -3,21 +3,31 @@ var DEFAULT_DEBOUNCE_MS = 1e3;
|
|
|
3
3
|
function createJoinReconnectScheduler(opts) {
|
|
4
4
|
const debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
5
5
|
let pending = null;
|
|
6
|
+
let queued = false;
|
|
7
|
+
function fire() {
|
|
8
|
+
try {
|
|
9
|
+
return opts.reconnect() !== false;
|
|
10
|
+
} catch (err) {
|
|
11
|
+
console.error(
|
|
12
|
+
"[join-reconnect] reconnect action failed:",
|
|
13
|
+
err?.message ?? String(err)
|
|
14
|
+
);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
6
18
|
return {
|
|
7
19
|
requestReconnect() {
|
|
8
20
|
if (pending !== null) return;
|
|
9
21
|
pending = setTimeout(() => {
|
|
10
22
|
pending = null;
|
|
11
|
-
|
|
12
|
-
opts.reconnect();
|
|
13
|
-
} catch (err) {
|
|
14
|
-
console.error(
|
|
15
|
-
"[join-reconnect] reconnect action failed:",
|
|
16
|
-
err?.message ?? String(err)
|
|
17
|
-
);
|
|
18
|
-
}
|
|
23
|
+
if (!fire()) queued = true;
|
|
19
24
|
}, debounceMs);
|
|
20
25
|
pending.unref?.();
|
|
26
|
+
},
|
|
27
|
+
notifyReady() {
|
|
28
|
+
if (!queued) return;
|
|
29
|
+
queued = false;
|
|
30
|
+
if (!fire()) queued = true;
|
|
21
31
|
}
|
|
22
32
|
};
|
|
23
33
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readHookStdin
|
|
3
|
+
} from "./chunk-LSUB6QMP.js";
|
|
1
4
|
import {
|
|
2
5
|
monitorHeartbeatPath,
|
|
3
6
|
nudgeSentinelPath
|
|
4
7
|
} from "./chunk-2TUAFAIW.js";
|
|
5
|
-
import {
|
|
6
|
-
readHookStdin
|
|
7
|
-
} from "./chunk-LSUB6QMP.js";
|
|
8
8
|
import {
|
|
9
9
|
controlTokenAuthHeaders
|
|
10
10
|
} from "./chunk-GI2CKKBL.js";
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WIZARD_RUNTIMES,
|
|
3
|
+
isWizardRuntime
|
|
4
|
+
} from "./chunk-LVL25VLO.js";
|
|
1
5
|
import {
|
|
2
6
|
clearRuntimeRecord,
|
|
3
7
|
readRecordedRuntime,
|
|
@@ -12,10 +16,6 @@ import {
|
|
|
12
16
|
import {
|
|
13
17
|
kojeeHomeDir
|
|
14
18
|
} from "./chunk-SQL56SEB.js";
|
|
15
|
-
import {
|
|
16
|
-
WIZARD_RUNTIMES,
|
|
17
|
-
isWizardRuntime
|
|
18
|
-
} from "./chunk-LVL25VLO.js";
|
|
19
19
|
import {
|
|
20
20
|
resolveSignatureEmission,
|
|
21
21
|
resolveWebhookConfig
|