kojee-mcp 0.7.5-staging.6 → 0.7.5-staging.8
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-6XWTUDWW.js → chunk-376ZI3J7.js} +5 -5
- package/dist/chunk-COM6435C.js +65 -0
- package/dist/{chunk-4ATSAH7H.js → chunk-HRDASFRF.js} +6 -5
- package/dist/{chunk-NE2F5CKS.js → chunk-LKGKDZSO.js} +9 -5
- package/dist/{chunk-FJUAMJHU.js → chunk-O4JQY7IF.js} +27 -1
- package/dist/{chunk-A4IOKD4Z.js → chunk-S35ZIVOL.js} +2 -4
- package/dist/{chunk-TCWIXG5C.js → chunk-XYDNRVOU.js} +1 -1
- package/dist/{chunk-CPOK642I.js → chunk-ZUF47CPP.js} +83 -21
- package/dist/cli.js +26 -15
- package/dist/{codex-prompt-submit-hook-J5PZEJSK.js → codex-prompt-submit-hook-ACDZSHHC.js} +1 -1
- package/dist/{codex-stop-hook-32W4BIOM.js → codex-stop-hook-M3LEWCNP.js} +1 -1
- package/dist/{connect-handler-NCHC6WPU.js → connect-handler-NJ4FZIAM.js} +65 -7
- package/dist/{doctor-RKLXZR2B.js → doctor-KMYKRYPE.js} +56 -3
- package/dist/{doctor-codex-PJFWIABF.js → doctor-codex-HB442SFA.js} +40 -6
- package/dist/{doctor-openclaw-LL6NSPLC.js → doctor-openclaw-IWY64I2E.js} +1 -1
- package/dist/index.js +4 -5
- package/dist/{install-JQNDGAAQ.js → install-NG366AOQ.js} +4 -4
- package/dist/{registry-DKWVVXJM.js → registry-NLRXVII7.js} +20 -17
- package/dist/{server-DU3LFS32.js → server-RK4UK6SP.js} +2 -3
- package/dist/{setup-handler-DA4465VH.js → setup-handler-I7ZEZTKW.js} +4 -5
- package/dist/{stop-hook-4MNOSDCB.js → stop-hook-Y34L3RCR.js} +1 -1
- package/dist/{tail-stream-G4WGTXDS.js → tail-stream-HG4BKJOG.js} +105 -5
- package/package.json +1 -1
- package/dist/chunk-5DHIUN73.js +0 -25
- package/dist/chunk-TMCNB4JH.js +0 -33
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
wrapNpxLauncher
|
|
3
|
+
} from "./chunk-D6JKFJ6A.js";
|
|
1
4
|
import {
|
|
2
5
|
buildHookCommand,
|
|
3
6
|
isKojeeHookCommand,
|
|
4
7
|
npxHookCommand
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import {
|
|
7
|
-
wrapNpxLauncher
|
|
8
|
-
} from "./chunk-D6JKFJ6A.js";
|
|
8
|
+
} from "./chunk-COM6435C.js";
|
|
9
9
|
import {
|
|
10
10
|
kojeeHomeDir
|
|
11
11
|
} from "./chunk-SQL56SEB.js";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "./chunk-U5HHHRXA.js";
|
|
15
15
|
import {
|
|
16
16
|
VERSION
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-O4JQY7IF.js";
|
|
18
18
|
|
|
19
19
|
// src/wizard/codex-config.ts
|
|
20
20
|
import fs from "fs";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/hooks/hook-command.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
function npxHookCommand(type) {
|
|
6
|
+
return `npx -y kojee-mcp hook --type=${type}`;
|
|
7
|
+
}
|
|
8
|
+
var NPX_CACHE_RE = /[\\/]_npx[\\/]/;
|
|
9
|
+
function resolveCliEntry(moduleUrl = import.meta.url) {
|
|
10
|
+
try {
|
|
11
|
+
const here = fileURLToPath(moduleUrl);
|
|
12
|
+
const candidate = path.join(path.dirname(here), "cli.js");
|
|
13
|
+
if (!fs.existsSync(candidate)) return null;
|
|
14
|
+
if (NPX_CACHE_RE.test(candidate)) return null;
|
|
15
|
+
return candidate;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function stableExecPath(execPath = process.execPath) {
|
|
21
|
+
const m = /^(.*)\/Cellar\/node(?:@[^/]+)?\/[^/]+\/bin\/node$/.exec(execPath);
|
|
22
|
+
if (!m || !m[1]) return execPath;
|
|
23
|
+
const alias = path.join(m[1], "bin", "node");
|
|
24
|
+
try {
|
|
25
|
+
if (!fs.existsSync(alias)) return execPath;
|
|
26
|
+
const real = fs.realpathSync(alias);
|
|
27
|
+
return /[\\/]Cellar[\\/]node(@[^/]+)?[\\/]/.test(real) ? alias : execPath;
|
|
28
|
+
} catch {
|
|
29
|
+
return execPath;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function buildHookCommand(type, opts = {}) {
|
|
33
|
+
const cliEntry = opts.cliEntry !== void 0 ? opts.cliEntry : resolveCliEntry();
|
|
34
|
+
if (!cliEntry) return npxHookCommand(type);
|
|
35
|
+
const execPath = stableExecPath(opts.execPath ?? process.execPath);
|
|
36
|
+
return `"${execPath}" "${cliEntry}" hook --type=${type}`;
|
|
37
|
+
}
|
|
38
|
+
var PINNED_HOOK_RE = /^"[^"]+" "[^"]+" hook --type=(stop|user-prompt-submit|codex-stop|codex-prompt-submit)$/;
|
|
39
|
+
function isKojeeHookCommand(command) {
|
|
40
|
+
if (command.startsWith("npx -y kojee-mcp hook --type=")) return true;
|
|
41
|
+
return PINNED_HOOK_RE.test(command);
|
|
42
|
+
}
|
|
43
|
+
function classifyHookCommand(command, deps = {}) {
|
|
44
|
+
if (command.startsWith("npx -y kojee-mcp hook --type=")) return "npx";
|
|
45
|
+
if (!PINNED_HOOK_RE.test(command)) return "foreign";
|
|
46
|
+
const paths = [...command.matchAll(/"([^"]+)"/g)].map((m) => m[1] ?? "");
|
|
47
|
+
const cliPath = paths[1] ?? "";
|
|
48
|
+
if (NPX_CACHE_RE.test(cliPath)) return "pinned-ephemeral";
|
|
49
|
+
const exists = deps.exists ?? ((p) => {
|
|
50
|
+
try {
|
|
51
|
+
return fs.existsSync(p);
|
|
52
|
+
} catch {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
if (cliPath && !exists(cliPath)) return "pinned-broken";
|
|
57
|
+
return "pinned-durable";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
npxHookCommand,
|
|
62
|
+
buildHookCommand,
|
|
63
|
+
isKojeeHookCommand,
|
|
64
|
+
classifyHookCommand
|
|
65
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
claudeCodeAdapter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XYDNRVOU.js";
|
|
4
4
|
import {
|
|
5
5
|
GatewayClient,
|
|
6
6
|
applyStableSessionId
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
createMcpServer,
|
|
13
13
|
startMcpServer
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-S35ZIVOL.js";
|
|
15
15
|
import {
|
|
16
16
|
findClaudeAncestorPid
|
|
17
17
|
} from "./chunk-XJEBJIQE.js";
|
|
@@ -295,9 +295,11 @@ async function startProxy(config) {
|
|
|
295
295
|
}
|
|
296
296
|
console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
|
|
297
297
|
let server;
|
|
298
|
-
const
|
|
298
|
+
const isHeadlessDaemon = !!process.env["KOJEE_HEADLESS"] || !!process.env["KOJEE_WEBHOOK_EXPECTED"];
|
|
299
|
+
const { selectDelivery } = await import("./registry-NLRXVII7.js");
|
|
299
300
|
const delivery = selectDelivery(adapter.runtime, {
|
|
300
301
|
supportsChannels: adapter.supportsChannels,
|
|
302
|
+
headless: isHeadlessDaemon,
|
|
301
303
|
// Per-window delivered mirror for the tandem_pending tool (codex only —
|
|
302
304
|
// other runtimes' deliveries ignore this even when passed).
|
|
303
305
|
...pendingState ? { onDelivered: (e) => pendingState.noteDelivered(e.tandem_id, e.cursor) } : {}
|
|
@@ -338,13 +340,12 @@ async function startProxy(config) {
|
|
|
338
340
|
}
|
|
339
341
|
server = createMcpServer(registry, adapter, tandemMembershipCount);
|
|
340
342
|
}
|
|
341
|
-
const isHeadlessDaemon = !!process.env["KOJEE_WEBHOOK_EXPECTED"];
|
|
342
343
|
if (!isHeadlessDaemon) {
|
|
343
344
|
process.stdin.on("end", () => shutdown("stdin end"));
|
|
344
345
|
process.stdin.on("close", () => shutdown("stdin close"));
|
|
345
346
|
} else {
|
|
346
347
|
console.error(
|
|
347
|
-
"[kojee-mcp] headless daemon (KOJEE_WEBHOOK_EXPECTED
|
|
348
|
+
"[kojee-mcp] headless daemon (KOJEE_HEADLESS / KOJEE_WEBHOOK_EXPECTED): stdin EOF ignored; lifecycle owned by the service manager (SIGTERM/SIGINT/SIGHUP)."
|
|
348
349
|
);
|
|
349
350
|
}
|
|
350
351
|
process.on("SIGHUP", () => shutdown("SIGHUP"));
|
|
@@ -87,18 +87,22 @@ function createCachedOpenclawWakeResolver(opts = {}) {
|
|
|
87
87
|
return resolution;
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
function openclawAgentUrl(wakeUrl) {
|
|
91
|
+
return wakeUrl.replace(/\/wake$/, "/agent");
|
|
92
|
+
}
|
|
93
|
+
async function postOpenclawAgent(target, body, deps = {}) {
|
|
91
94
|
const doFetch = deps.fetch ?? fetch;
|
|
95
|
+
const url = openclawAgentUrl(target.url);
|
|
92
96
|
const controller = new AbortController();
|
|
93
97
|
const timer = setTimeout(() => controller.abort(), deps.timeoutMs ?? 4e3);
|
|
94
98
|
try {
|
|
95
|
-
const res = await doFetch(
|
|
99
|
+
const res = await doFetch(url, {
|
|
96
100
|
method: "POST",
|
|
97
101
|
headers: { "content-type": "application/json", authorization: `Bearer ${target.token}` },
|
|
98
|
-
body: JSON.stringify(body),
|
|
102
|
+
body: JSON.stringify({ name: "Kojee", wakeMode: "now", ...body }),
|
|
99
103
|
signal: controller.signal
|
|
100
104
|
});
|
|
101
|
-
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status, error: `
|
|
105
|
+
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status, error: `agent POST ${res.status}` };
|
|
102
106
|
} catch (err) {
|
|
103
107
|
return { ok: false, error: err.message };
|
|
104
108
|
} finally {
|
|
@@ -109,5 +113,5 @@ async function postOpenclawWake(target, body, deps = {}) {
|
|
|
109
113
|
export {
|
|
110
114
|
resolveOpenclawWakeTarget,
|
|
111
115
|
createCachedOpenclawWakeResolver,
|
|
112
|
-
|
|
116
|
+
postOpenclawAgent
|
|
113
117
|
};
|
|
@@ -1,7 +1,32 @@
|
|
|
1
|
+
// src/version.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
var FALLBACK_VERSION = "0.0.0-unknown";
|
|
6
|
+
function resolveVersion() {
|
|
7
|
+
try {
|
|
8
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const parsed = JSON.parse(
|
|
10
|
+
fs.readFileSync(path.join(here, "..", "package.json"), "utf8")
|
|
11
|
+
);
|
|
12
|
+
return typeof parsed?.version === "string" && parsed.version ? parsed.version : FALLBACK_VERSION;
|
|
13
|
+
} catch (err) {
|
|
14
|
+
process.stderr.write(
|
|
15
|
+
`kojee-mcp: could not resolve version from package.json, falling back to ${FALLBACK_VERSION}: ${String(err)}
|
|
16
|
+
`
|
|
17
|
+
);
|
|
18
|
+
return FALLBACK_VERSION;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
var VERSION = resolveVersion();
|
|
22
|
+
|
|
1
23
|
// src/tandem/recipe.ts
|
|
2
24
|
var SEND_BODY_PARAM = "body";
|
|
25
|
+
function monitorPackageSpec() {
|
|
26
|
+
return VERSION && VERSION !== "0.0.0-unknown" ? `kojee-mcp@${VERSION}` : "kojee-mcp";
|
|
27
|
+
}
|
|
3
28
|
function buildMonitorCommand(logPath) {
|
|
4
|
-
return `npx
|
|
29
|
+
return `npx ${monitorPackageSpec()} tail --wake-only "${logPath}"`;
|
|
5
30
|
}
|
|
6
31
|
function buildMonitorSpawn(logPath) {
|
|
7
32
|
return `Monitor(command=\`${buildMonitorCommand(logPath)}\`, persistent=true, description="kojee Tandem events")`;
|
|
@@ -32,6 +57,7 @@ var CODEX_LISTEN_CAP_MS = 8e3;
|
|
|
32
57
|
var CODEX_WAKE_BELL = "[kojee] Tandem events may be pending. Call tandem_pending now and drain each room it lists (tandem_messages(id, since=cursor)), then reply in the room with a normal message (kind=message \u2014 never status). If it returns none for you, ignore this.";
|
|
33
58
|
|
|
34
59
|
export {
|
|
60
|
+
VERSION,
|
|
35
61
|
buildMonitorSpawn,
|
|
36
62
|
buildReplyRecipe,
|
|
37
63
|
buildCatchUpNote,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
VERSION
|
|
3
|
-
} from "./chunk-5DHIUN73.js";
|
|
4
|
-
import {
|
|
2
|
+
VERSION,
|
|
5
3
|
buildCatchUpNote,
|
|
6
4
|
buildCondensedTandemRules,
|
|
7
5
|
buildMonitorSpawn,
|
|
8
6
|
buildReplyRecipe
|
|
9
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-O4JQY7IF.js";
|
|
10
8
|
import {
|
|
11
9
|
translateToolCallResult
|
|
12
10
|
} from "./chunk-PPTKGWFF.js";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
isPlaceholderWebhookUrl,
|
|
6
6
|
removeCodexConfig,
|
|
7
7
|
writeCodexConfig
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-376ZI3J7.js";
|
|
9
9
|
import {
|
|
10
10
|
removeOpenclawMcpServer,
|
|
11
11
|
writeOpenclawMcpConfig
|
|
@@ -48,14 +48,12 @@ import {
|
|
|
48
48
|
secureFile,
|
|
49
49
|
unlinkSyncRetry
|
|
50
50
|
} from "./chunk-U5HHHRXA.js";
|
|
51
|
-
import {
|
|
52
|
-
VERSION
|
|
53
|
-
} from "./chunk-5DHIUN73.js";
|
|
54
51
|
import {
|
|
55
52
|
CODEX_LISTEN_CAP_MS,
|
|
53
|
+
VERSION,
|
|
56
54
|
buildCondensedTandemRules,
|
|
57
55
|
buildWebhookReceiverNote
|
|
58
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-O4JQY7IF.js";
|
|
59
57
|
|
|
60
58
|
// src/wizard/pair-slot.ts
|
|
61
59
|
import fs from "fs";
|
|
@@ -424,9 +422,18 @@ function installBundledPayload(opts) {
|
|
|
424
422
|
// src/wizard/service.ts
|
|
425
423
|
import fs5 from "fs";
|
|
426
424
|
import path5 from "path";
|
|
425
|
+
function shQuote(value) {
|
|
426
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
427
|
+
}
|
|
428
|
+
function wrapperEnvEntries(spec) {
|
|
429
|
+
const marker = spec.wrapperEnv ?? { KOJEE_WEBHOOK_EXPECTED: "1" };
|
|
430
|
+
return [["KOJEE_RUNTIME", spec.runtime], ...Object.entries(marker)];
|
|
431
|
+
}
|
|
427
432
|
function daemonSourceExecCommand(spec) {
|
|
428
433
|
const target = spec.nodePath ? `${spec.nodePath} ${spec.binPath}` : spec.binPath;
|
|
429
|
-
|
|
434
|
+
const withArgs = spec.binArgs && spec.binArgs.length > 0 ? `${target} ${spec.binArgs.map(shQuote).join(" ")}` : target;
|
|
435
|
+
const source = spec.envFile ? `set -a; . ${spec.envFile}; set +a; ` : "";
|
|
436
|
+
return `${source}exec ${withArgs}`;
|
|
430
437
|
}
|
|
431
438
|
function xmlEscape(value) {
|
|
432
439
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -445,7 +452,7 @@ function systemdUnit(spec) {
|
|
|
445
452
|
// otherwise kill the marker too. It lets startProxy tell the "webhook was
|
|
446
453
|
// meant to be here" daemon apart from the tools-only child (which never sets
|
|
447
454
|
// it) when the sourced URL is absent → null delivery.
|
|
448
|
-
`Environment
|
|
455
|
+
`Environment=${wrapperEnvEntries(spec).map(([k, v]) => `${k}=${v}`).join(" ")}`,
|
|
449
456
|
// §2a: source the env file inside a shell wrapper (replaces EnvironmentFile=).
|
|
450
457
|
`ExecStart=/bin/sh -c '${daemonSourceExecCommand(spec)}'`,
|
|
451
458
|
"Restart=always",
|
|
@@ -480,7 +487,7 @@ function launchdPlist(spec, label) {
|
|
|
480
487
|
" </array>",
|
|
481
488
|
// §2b: KOJEE_WEBHOOK_EXPECTED in the wrapper env (NOT the sourced file).
|
|
482
489
|
" <key>EnvironmentVariables</key>",
|
|
483
|
-
` <dict
|
|
490
|
+
` <dict>${wrapperEnvEntries(spec).map(([k, v]) => `<key>${xmlEscape(k)}</key><string>${xmlEscape(v)}</string>`).join("")}</dict>`,
|
|
484
491
|
" <key>RunAtLoad</key><true/>",
|
|
485
492
|
" <key>KeepAlive</key><true/>",
|
|
486
493
|
"</dict>",
|
|
@@ -532,6 +539,15 @@ function writeService(platform, spec) {
|
|
|
532
539
|
}
|
|
533
540
|
return plan;
|
|
534
541
|
}
|
|
542
|
+
function removeService(platform, spec) {
|
|
543
|
+
const plan = planService(platform, spec);
|
|
544
|
+
let removed = false;
|
|
545
|
+
if (plan.unitPath && fs5.existsSync(plan.unitPath)) {
|
|
546
|
+
fs5.unlinkSync(plan.unitPath);
|
|
547
|
+
removed = true;
|
|
548
|
+
}
|
|
549
|
+
return { removed, deactivateCmd: plan.deactivateCmd };
|
|
550
|
+
}
|
|
535
551
|
|
|
536
552
|
// src/wizard/installers/hermes.ts
|
|
537
553
|
function installHermes(inp) {
|
|
@@ -619,6 +635,16 @@ function installOpenclaw(inp) {
|
|
|
619
635
|
...inp.pairedConfigPath ? { pairedConfigPath: inp.pairedConfigPath } : {},
|
|
620
636
|
...inp.now ? { now: inp.now } : {}
|
|
621
637
|
});
|
|
638
|
+
const platform = inp.platform ?? process.platform;
|
|
639
|
+
const svc = !inp.skipService && inp.binPath && inp.homeDir ? writeService(platform, {
|
|
640
|
+
serviceName: "kojee-openclaw",
|
|
641
|
+
binPath: inp.binPath,
|
|
642
|
+
...inp.nodePath ? { nodePath: inp.nodePath } : {},
|
|
643
|
+
runtime: "openclaw",
|
|
644
|
+
homeDir: inp.homeDir,
|
|
645
|
+
binArgs: ["--paired-config", inp.pairedConfigPath ?? perRuntimeConfigPath("openclaw")],
|
|
646
|
+
wrapperEnv: { KOJEE_HEADLESS: "1" }
|
|
647
|
+
}) : null;
|
|
622
648
|
const lines = [];
|
|
623
649
|
if (r.backedUp) {
|
|
624
650
|
lines.push(
|
|
@@ -627,17 +653,22 @@ function installOpenclaw(inp) {
|
|
|
627
653
|
);
|
|
628
654
|
}
|
|
629
655
|
lines.push(
|
|
630
|
-
"Configured runtime: openclaw (MCP server + /wake
|
|
631
|
-
"Wake mode: kojee-
|
|
632
|
-
" OpenClaw gateway /hooks/
|
|
633
|
-
|
|
656
|
+
"Configured runtime: openclaw (MCP server + standing /hooks/agent wake daemon)",
|
|
657
|
+
"Wake mode: the kojee-openclaw DAEMON holds the Tandem SSE stream and, on an @mention,",
|
|
658
|
+
" POSTs the local OpenClaw gateway /hooks/agent (Bearer hooks.token) \u2192 a FULL",
|
|
659
|
+
" agent run (kojee tools mounted) so the agent can reply via tandem_send.",
|
|
660
|
+
` mcp config: ${inp.openclawConfigPath} (mcp.servers.kojee \u2014 per-run tools; no SSE stream)`,
|
|
634
661
|
inp.pairedConfigPath ? ` credentials: per-runtime paired slot (${inp.pairedConfigPath})` : ` credentials: shared ~/.kojee/config.json (paired)`,
|
|
635
662
|
` hooks token: ${r.hooksTokenGenerated ? "generated + hooks.enabled=true (new)" : "preserved existing"}`,
|
|
663
|
+
svc ? svc.supported ? ` wake daemon: ${svc.unitPath}` : ` wake daemon: manual \u2014 ${svc.note ?? "unsupported platform"}` : " wake daemon: NOT installed (no launch path resolved) \u2014 run connect from the CLI",
|
|
636
664
|
"",
|
|
637
665
|
"Next steps (openclaw):",
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
666
|
+
...svc && svc.supported ? [
|
|
667
|
+
` - Start the standing wake daemon: ${svc.activateCmd}`,
|
|
668
|
+
" (it holds the SSE stream so an IDLE agent is still woken on @mentions)."
|
|
669
|
+
] : svc ? [` - Start the standing wake daemon: ${svc.activateCmd}`] : [],
|
|
670
|
+
" - Ensure the OpenClaw gateway is running (the daemon POSTs /hooks/agent to it).",
|
|
671
|
+
" - Restart the OpenClaw gateway to spawn the kojee MCP tools server: openclaw gateway restart",
|
|
641
672
|
" - Verify: openclaw mcp list (kojee should be listed) / kojee-mcp doctor",
|
|
642
673
|
"",
|
|
643
674
|
// Belt-and-suspenders: the same condensed rules ride the kojee MCP server's
|
|
@@ -652,11 +683,21 @@ function installOpenclaw(inp) {
|
|
|
652
683
|
}
|
|
653
684
|
function uninstallOpenclaw(inp) {
|
|
654
685
|
const removed = removeOpenclawMcpServer(inp.openclawConfigPath);
|
|
686
|
+
const svc = inp.homeDir ? removeService(inp.platform ?? process.platform, {
|
|
687
|
+
serviceName: "kojee-openclaw",
|
|
688
|
+
binPath: "",
|
|
689
|
+
runtime: "openclaw",
|
|
690
|
+
homeDir: inp.homeDir
|
|
691
|
+
}) : null;
|
|
655
692
|
const lines = [
|
|
656
|
-
"Uninstalling runtime: openclaw (MCP server +
|
|
693
|
+
"Uninstalling runtime: openclaw (MCP server + wake daemon)",
|
|
657
694
|
removed ? ` removed mcp.servers.kojee from ${inp.openclawConfigPath} (hooks + siblings preserved)` : ` no mcp.servers.kojee in ${inp.openclawConfigPath} \u2014 nothing to remove`,
|
|
695
|
+
...svc ? [
|
|
696
|
+
svc.removed ? ` removed the kojee-openclaw wake-daemon unit (stop it: ${svc.deactivateCmd})` : " no kojee-openclaw wake-daemon unit found"
|
|
697
|
+
] : [],
|
|
658
698
|
"",
|
|
659
699
|
"Next steps (openclaw):",
|
|
700
|
+
" - Stop the wake daemon if still running (see the deactivate command above).",
|
|
660
701
|
" - Restart the OpenClaw gateway: openclaw gateway restart",
|
|
661
702
|
" - hooks.enabled/token were left intact (you may use hooks for other integrations)."
|
|
662
703
|
];
|
|
@@ -829,7 +870,7 @@ function whatHappensNext(runtime, opts) {
|
|
|
829
870
|
return lines.join("\n");
|
|
830
871
|
}
|
|
831
872
|
async function configureClaudeCode(opts) {
|
|
832
|
-
const { runInit } = await import("./install-
|
|
873
|
+
const { runInit } = await import("./install-NG366AOQ.js");
|
|
833
874
|
const report = runInit({
|
|
834
875
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
835
876
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
@@ -1088,18 +1129,35 @@ function openclawConfigPath(opts) {
|
|
|
1088
1129
|
}
|
|
1089
1130
|
function configureOpenclaw(opts) {
|
|
1090
1131
|
const runtime = "openclaw";
|
|
1132
|
+
const daemonExec = resolveDaemonExec();
|
|
1091
1133
|
const install = installOpenclaw({
|
|
1092
1134
|
openclawConfigPath: openclawConfigPath(opts),
|
|
1093
|
-
...opts.pairedConfigPath ? { pairedConfigPath: opts.pairedConfigPath } : {}
|
|
1135
|
+
...opts.pairedConfigPath ? { pairedConfigPath: opts.pairedConfigPath } : {},
|
|
1136
|
+
// Launch coords for the standing kojee-openclaw wake daemon (holds the SSE
|
|
1137
|
+
// stream so an idle agent is reachable). Absent → installer writes only the
|
|
1138
|
+
// MCP config (the shape tests).
|
|
1139
|
+
binPath: daemonExec.binPath,
|
|
1140
|
+
nodePath: daemonExec.nodePath,
|
|
1141
|
+
homeDir: kojeeHomeDir(),
|
|
1142
|
+
platform: process.platform
|
|
1094
1143
|
});
|
|
1095
1144
|
recordRuntime(runtime);
|
|
1096
|
-
|
|
1145
|
+
const lines = [install.output];
|
|
1146
|
+
if (daemonExec.ephemeral) {
|
|
1147
|
+
lines.push(
|
|
1148
|
+
"",
|
|
1149
|
+
"\u26A0 DURABILITY: this ran from an ephemeral npx cache, so the wake daemon points at a",
|
|
1150
|
+
" path npx will eventually delete (the daemon would then fail to start). For a durable",
|
|
1151
|
+
" daemon: npm i -g kojee-mcp && kojee-mcp connect <code> --runtime openclaw"
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
return { runtime, output: lines.join("\n"), exitCode: install.exitCode };
|
|
1097
1155
|
}
|
|
1098
1156
|
async function runWizardUninstall(runtime, opts) {
|
|
1099
1157
|
const effective = opts.runtime !== void 0 ? runtime : readRecordedRuntime() ?? runtime;
|
|
1100
1158
|
const lines = [`Uninstalling runtime: ${effective}`];
|
|
1101
1159
|
if (effective === "claude-code") {
|
|
1102
|
-
const { runUninstall } = await import("./install-
|
|
1160
|
+
const { runUninstall } = await import("./install-NG366AOQ.js");
|
|
1103
1161
|
const report = runUninstall({
|
|
1104
1162
|
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
1105
1163
|
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {}
|
|
@@ -1123,7 +1181,11 @@ async function runWizardUninstall(runtime, opts) {
|
|
|
1123
1181
|
lines.push(` config.toml [mcp_servers.kojee]: ${removed.mcpServer ? "removed" : "not found"}`);
|
|
1124
1182
|
lines.push(` hooks.json Stop: ${removed.stopHook ? "removed" : "not found"}`);
|
|
1125
1183
|
} else if (effective === "openclaw") {
|
|
1126
|
-
const un = uninstallOpenclaw({
|
|
1184
|
+
const un = uninstallOpenclaw({
|
|
1185
|
+
openclawConfigPath: openclawConfigPath(opts),
|
|
1186
|
+
homeDir: kojeeHomeDir(),
|
|
1187
|
+
platform: process.platform
|
|
1188
|
+
});
|
|
1127
1189
|
lines.push(un.output);
|
|
1128
1190
|
} else {
|
|
1129
1191
|
const hermesConfig = defaultHermesConfigPath(kojeeHomeDir());
|
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
} from "./chunk-EIAUW6KO.js";
|
|
5
5
|
import {
|
|
6
6
|
startProxy
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-HRDASFRF.js";
|
|
8
|
+
import "./chunk-XYDNRVOU.js";
|
|
9
9
|
import {
|
|
10
10
|
pairedConfigPath
|
|
11
11
|
} from "./chunk-5SZHXYPK.js";
|
|
@@ -18,11 +18,10 @@ import {
|
|
|
18
18
|
deriveKeystorePath
|
|
19
19
|
} from "./chunk-6G6YYST6.js";
|
|
20
20
|
import "./chunk-U5HHHRXA.js";
|
|
21
|
-
import "./chunk-
|
|
21
|
+
import "./chunk-S35ZIVOL.js";
|
|
22
22
|
import {
|
|
23
23
|
VERSION
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-FJUAMJHU.js";
|
|
24
|
+
} from "./chunk-O4JQY7IF.js";
|
|
26
25
|
import "./chunk-PPTKGWFF.js";
|
|
27
26
|
import "./chunk-XJEBJIQE.js";
|
|
28
27
|
import "./chunk-SRLD2UY7.js";
|
|
@@ -48,7 +47,7 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
48
47
|
program.command("connect <code>").description(
|
|
49
48
|
"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
49
|
).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-
|
|
50
|
+
const { runConnect } = await import("./connect-handler-NJ4FZIAM.js");
|
|
52
51
|
const result = await runConnect({
|
|
53
52
|
code,
|
|
54
53
|
runtime: opts.runtime,
|
|
@@ -58,12 +57,21 @@ program.command("connect <code>").description(
|
|
|
58
57
|
console.error(result.output);
|
|
59
58
|
process.exit(result.exitCode);
|
|
60
59
|
});
|
|
60
|
+
program.command("reconnect").description(
|
|
61
|
+
"Re-run configure for an already-paired runtime, KEEPING the token + enrolled key (no re-pair). Re-writes configs/hooks to this install and refreshes the agent instructions."
|
|
62
|
+
).option("--runtime <id>", "Target runtime (defaults to the recorded runtime from the last connect/init)").action(async (opts) => {
|
|
63
|
+
const { runReconnect } = await import("./connect-handler-NJ4FZIAM.js");
|
|
64
|
+
const result = await runReconnect({ ...opts.runtime ? { runtime: opts.runtime } : {} });
|
|
65
|
+
for (const m of result.messages) console.error(m);
|
|
66
|
+
console.error(result.output);
|
|
67
|
+
process.exit(result.exitCode);
|
|
68
|
+
});
|
|
61
69
|
program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption(
|
|
62
70
|
"--type <type>",
|
|
63
71
|
"Hook type: stop, user-prompt-submit, codex-stop, or codex-prompt-submit"
|
|
64
72
|
).action(async (opts) => {
|
|
65
73
|
if (opts.type === "stop") {
|
|
66
|
-
const { runStopHook } = await import("./stop-hook-
|
|
74
|
+
const { runStopHook } = await import("./stop-hook-Y34L3RCR.js");
|
|
67
75
|
await runStopHook();
|
|
68
76
|
process.exit(0);
|
|
69
77
|
} else if (opts.type === "user-prompt-submit") {
|
|
@@ -71,11 +79,11 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
|
|
|
71
79
|
await runUserPromptSubmitHook();
|
|
72
80
|
process.exit(0);
|
|
73
81
|
} else if (opts.type === "codex-stop") {
|
|
74
|
-
const { runCodexStopHook } = await import("./codex-stop-hook-
|
|
82
|
+
const { runCodexStopHook } = await import("./codex-stop-hook-M3LEWCNP.js");
|
|
75
83
|
await runCodexStopHook();
|
|
76
84
|
process.exit(0);
|
|
77
85
|
} else if (opts.type === "codex-prompt-submit") {
|
|
78
|
-
const { runCodexPromptSubmitHook } = await import("./codex-prompt-submit-hook-
|
|
86
|
+
const { runCodexPromptSubmitHook } = await import("./codex-prompt-submit-hook-ACDZSHHC.js");
|
|
79
87
|
await runCodexPromptSubmitHook();
|
|
80
88
|
process.exit(0);
|
|
81
89
|
} else {
|
|
@@ -86,7 +94,7 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
|
|
|
86
94
|
}
|
|
87
95
|
});
|
|
88
96
|
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) => {
|
|
89
|
-
const { installHooks, uninstallHooks } = await import("./install-
|
|
97
|
+
const { installHooks, uninstallHooks } = await import("./install-NG366AOQ.js");
|
|
90
98
|
if (opts.uninstall) {
|
|
91
99
|
const removed = uninstallHooks({ hooksPath: opts.hooksPath });
|
|
92
100
|
console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
|
|
@@ -114,17 +122,20 @@ program.command("send <tandem_id>").description(
|
|
|
114
122
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
115
123
|
process.exit(exitCode);
|
|
116
124
|
});
|
|
117
|
-
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").
|
|
118
|
-
|
|
125
|
+
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").option(
|
|
126
|
+
"--wake-only",
|
|
127
|
+
"Suppress routine heartbeat warn/recover telemetry; surface only a sustained stream outage"
|
|
128
|
+
).action(async (filePath, opts) => {
|
|
129
|
+
const { runTail } = await import("./tail-stream-HG4BKJOG.js");
|
|
119
130
|
try {
|
|
120
|
-
await runTail(filePath);
|
|
131
|
+
await runTail(filePath, { wakeOnly: opts.wakeOnly === true });
|
|
121
132
|
} catch (err) {
|
|
122
133
|
console.error("[kojee-mcp tail] Error:", err.message);
|
|
123
134
|
process.exit(1);
|
|
124
135
|
}
|
|
125
136
|
});
|
|
126
137
|
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 () => {
|
|
127
|
-
const { runDoctor } = await import("./doctor-
|
|
138
|
+
const { runDoctor } = await import("./doctor-KMYKRYPE.js");
|
|
128
139
|
const code = await runDoctor();
|
|
129
140
|
process.exit(code);
|
|
130
141
|
});
|
|
@@ -143,7 +154,7 @@ function addInstallOptions(cmd, runtimeHelp) {
|
|
|
143
154
|
function makeInstallAction(verb) {
|
|
144
155
|
return async (opts) => {
|
|
145
156
|
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
146
|
-
const { runSetup, resolvePairCode } = await import("./setup-handler-
|
|
157
|
+
const { runSetup, resolvePairCode } = await import("./setup-handler-I7ZEZTKW.js");
|
|
147
158
|
const pairCode = resolvePairCode(opts);
|
|
148
159
|
const result = await runSetup({
|
|
149
160
|
verb,
|
|
@@ -2,17 +2,19 @@ import {
|
|
|
2
2
|
DEFAULT_BROKER_URL,
|
|
3
3
|
reconcileConnectPairSlot,
|
|
4
4
|
runWizard
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-ZUF47CPP.js";
|
|
6
|
+
import "./chunk-376ZI3J7.js";
|
|
7
7
|
import "./chunk-E6WMFMM2.js";
|
|
8
8
|
import "./chunk-GEGUWQYT.js";
|
|
9
9
|
import "./chunk-77HWBSRH.js";
|
|
10
|
-
import "./chunk-TMCNB4JH.js";
|
|
11
10
|
import "./chunk-D6JKFJ6A.js";
|
|
11
|
+
import "./chunk-COM6435C.js";
|
|
12
12
|
import {
|
|
13
13
|
CONNECT_RUNTIMES
|
|
14
14
|
} from "./chunk-EIH2LNF4.js";
|
|
15
|
-
import
|
|
15
|
+
import {
|
|
16
|
+
readRecordedRuntime
|
|
17
|
+
} from "./chunk-RDLF4NQC.js";
|
|
16
18
|
import "./chunk-SQL56SEB.js";
|
|
17
19
|
import "./chunk-V5VZPYMZ.js";
|
|
18
20
|
import {
|
|
@@ -30,10 +32,10 @@ import {
|
|
|
30
32
|
deriveKeystorePath
|
|
31
33
|
} from "./chunk-6G6YYST6.js";
|
|
32
34
|
import "./chunk-U5HHHRXA.js";
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-FJUAMJHU.js";
|
|
35
|
+
import "./chunk-O4JQY7IF.js";
|
|
35
36
|
|
|
36
37
|
// src/wizard/connect-handler.ts
|
|
38
|
+
import fs from "fs";
|
|
37
39
|
import os from "os";
|
|
38
40
|
import path from "path";
|
|
39
41
|
function absConfigPath(runtime) {
|
|
@@ -140,7 +142,63 @@ Start (or restart) the hermes daemon to pick up the connection.`,
|
|
|
140
142
|
exitCode: 0
|
|
141
143
|
};
|
|
142
144
|
}
|
|
145
|
+
async function runReconnect(opts = {}) {
|
|
146
|
+
const messages = [];
|
|
147
|
+
const runtime = opts.runtime ?? readRecordedRuntime() ?? null;
|
|
148
|
+
if (!runtime) {
|
|
149
|
+
return {
|
|
150
|
+
messages,
|
|
151
|
+
output: `reconnect: no runtime recorded on this box \u2014 pass --runtime (${CONNECT_RUNTIMES.join(" | ")} | hermes), or run \`kojee-mcp connect <code> --runtime <id>\` first.`,
|
|
152
|
+
exitCode: 2
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const isHermes = runtime === "hermes";
|
|
156
|
+
if (!isHermes && !CONNECT_RUNTIMES.includes(runtime)) {
|
|
157
|
+
return {
|
|
158
|
+
messages,
|
|
159
|
+
output: `reconnect supports: ${CONNECT_RUNTIMES.join(", ")}, hermes. Unknown --runtime "${runtime}".`,
|
|
160
|
+
exitCode: 2
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const configPath = isHermes ? pairedConfigPath() : absConfigPath(runtime);
|
|
164
|
+
const paired = loadPairedConfig(configPath);
|
|
165
|
+
if (!paired?.token) {
|
|
166
|
+
return {
|
|
167
|
+
messages,
|
|
168
|
+
output: `reconnect: no paired config at ${configPath} \u2014 nothing to keep. Run \`kojee-mcp connect <code> --runtime ${runtime}\` to pair first.`,
|
|
169
|
+
exitCode: 2
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const keystorePath = isHermes ? defaultPairedKeystorePath() : deriveKeystorePath(paired.token);
|
|
173
|
+
if (!fs.existsSync(keystorePath)) {
|
|
174
|
+
messages.push(
|
|
175
|
+
`\u26A0 enrolled keystore MISSING at ${keystorePath} \u2014 configs will be refreshed, but the proxy cannot authenticate with a new key on this token (backend rejects double-enroll). If sends fail, re-pair with a fresh code.`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
const wizard = opts.runWizard ?? runWizard;
|
|
179
|
+
const wiz = await wizard({
|
|
180
|
+
runtime,
|
|
181
|
+
...isHermes ? {} : { pairedConfigPath: configPath },
|
|
182
|
+
interactive: false
|
|
183
|
+
});
|
|
184
|
+
if (wiz.exitCode !== 0) {
|
|
185
|
+
return { messages, output: wiz.output, exitCode: wiz.exitCode };
|
|
186
|
+
}
|
|
187
|
+
const label = identityLabel(paired);
|
|
188
|
+
return {
|
|
189
|
+
messages,
|
|
190
|
+
output: [
|
|
191
|
+
`\u21BB reconfigured ${runtime} as ${label} \u2014 token KEPT (no re-pair, no key re-enrollment).`,
|
|
192
|
+
"Configs + hooks re-written to this install; agent instructions refreshed below.",
|
|
193
|
+
`Restart ${runtime} to pick up the changes.`,
|
|
194
|
+
"",
|
|
195
|
+
wiz.output
|
|
196
|
+
].join("\n"),
|
|
197
|
+
exitCode: 0
|
|
198
|
+
};
|
|
199
|
+
}
|
|
143
200
|
export {
|
|
144
201
|
runConnect,
|
|
145
|
-
runConnectHermes
|
|
202
|
+
runConnectHermes,
|
|
203
|
+
runReconnect
|
|
146
204
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyHookCommand,
|
|
3
|
+
isKojeeHookCommand
|
|
4
|
+
} from "./chunk-COM6435C.js";
|
|
1
5
|
import "./chunk-XLKGPGZT.js";
|
|
2
6
|
import {
|
|
3
7
|
loadControlToken
|
|
@@ -18,7 +22,7 @@ import "./chunk-U5HHHRXA.js";
|
|
|
18
22
|
import {
|
|
19
23
|
buildMonitorSpawn,
|
|
20
24
|
buildReplyRecipe
|
|
21
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-O4JQY7IF.js";
|
|
22
26
|
import {
|
|
23
27
|
deriveDiscoveryKey,
|
|
24
28
|
findClaudeAncestorPid
|
|
@@ -149,6 +153,53 @@ async function resolveDefaultPairedScope() {
|
|
|
149
153
|
}
|
|
150
154
|
return { scope: resolution.scope, present, ...runtime ? { runtime } : {} };
|
|
151
155
|
}
|
|
156
|
+
function defaultReadHookCommands() {
|
|
157
|
+
try {
|
|
158
|
+
const p = path.join(os.homedir(), ".claude", "settings.json");
|
|
159
|
+
const raw = fs.readFileSync(p, "utf8");
|
|
160
|
+
const cfg = JSON.parse(raw);
|
|
161
|
+
const out = [];
|
|
162
|
+
for (const event of ["Stop", "UserPromptSubmit"]) {
|
|
163
|
+
for (const matcher of cfg.hooks?.[event] ?? []) {
|
|
164
|
+
for (const h of matcher.hooks ?? []) {
|
|
165
|
+
if (typeof h.command === "string" && isKojeeHookCommand(h.command)) out.push(h.command);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
var HOOK_REPIN_FIX = "fix: `npm i -g kojee-mcp` then re-run `kojee-mcp setup` (re-pins in place, no re-pair)";
|
|
175
|
+
function hookPinCheck(commands) {
|
|
176
|
+
const name = "hook pin (settings.json Stop/UserPromptSubmit)";
|
|
177
|
+
if (commands === null) {
|
|
178
|
+
return { name, ok: "warn", detail: `no ~/.claude/settings.json readable \u2014 run \`kojee-mcp setup\` to install the wake hooks` };
|
|
179
|
+
}
|
|
180
|
+
if (commands.length === 0) {
|
|
181
|
+
return { name, ok: "warn", detail: `no kojee hooks installed \u2014 run \`kojee-mcp setup\` (the stop-hook is the Monitor self-heal)` };
|
|
182
|
+
}
|
|
183
|
+
const states = commands.map((c) => classifyHookCommand(c));
|
|
184
|
+
if (states.includes("pinned-broken")) {
|
|
185
|
+
return { name, ok: false, detail: `pinned cli.js is GONE \u2014 hooks are failing with exit 127 RIGHT NOW; ${HOOK_REPIN_FIX}` };
|
|
186
|
+
}
|
|
187
|
+
if (states.includes("pinned-ephemeral")) {
|
|
188
|
+
return {
|
|
189
|
+
name,
|
|
190
|
+
ok: false,
|
|
191
|
+
detail: `pinned INTO the npx cache (~/.npm/_npx/\u2026) \u2014 npx cache GC will silently kill the hooks (exit 127 every turn); ${HOOK_REPIN_FIX}`
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (states.includes("npx")) {
|
|
195
|
+
return {
|
|
196
|
+
name,
|
|
197
|
+
ok: "warn",
|
|
198
|
+
detail: `unpinned (npx -y) \u2014 pays npx resolution every turn and can stall on a registry fetch; ${HOOK_REPIN_FIX}`
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { name, ok: true, detail: `pinned to a durable binary (${commands.length} hook${commands.length === 1 ? "" : "s"})` };
|
|
202
|
+
}
|
|
152
203
|
async function collectDoctorReport(deps = {}) {
|
|
153
204
|
const fetchFn = deps.fetchFn ?? fetch;
|
|
154
205
|
const isPidAlive = deps.isPidAlive ?? defaultIsPidAlive;
|
|
@@ -183,6 +234,8 @@ async function collectDoctorReport(deps = {}) {
|
|
|
183
234
|
detail: pairedScope.present ? "present (legacy global slot)" : "MISSING \u2014 run `kojee-mcp connect <code> --runtime <id>` (or `kojee-mcp pair`)"
|
|
184
235
|
});
|
|
185
236
|
}
|
|
237
|
+
const hookCommands = (deps.readHookCommands ?? defaultReadHookCommands)();
|
|
238
|
+
checks.push(hookPinCheck(hookCommands));
|
|
186
239
|
let logPath = null;
|
|
187
240
|
if (!discovery) {
|
|
188
241
|
checks.push({
|
|
@@ -354,13 +407,13 @@ function formatDoctorReport(report) {
|
|
|
354
407
|
async function runDoctor() {
|
|
355
408
|
const { readRecordedRuntime } = await import("./runtime-record-OXRLTOLC.js");
|
|
356
409
|
if (readRecordedRuntime() === "codex") {
|
|
357
|
-
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-
|
|
410
|
+
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-HB442SFA.js");
|
|
358
411
|
const report2 = collectCodexDoctorReport();
|
|
359
412
|
console.error(formatCodexDoctorReport(report2));
|
|
360
413
|
return report2.verdict === "broken" ? 1 : 0;
|
|
361
414
|
}
|
|
362
415
|
if (readRecordedRuntime() === "openclaw") {
|
|
363
|
-
const { collectOpenclawDoctorReport, formatOpenclawDoctorReport } = await import("./doctor-openclaw-
|
|
416
|
+
const { collectOpenclawDoctorReport, formatOpenclawDoctorReport } = await import("./doctor-openclaw-IWY64I2E.js");
|
|
364
417
|
const report2 = collectOpenclawDoctorReport();
|
|
365
418
|
console.error(formatOpenclawDoctorReport(report2));
|
|
366
419
|
return report2.verdict === "broken" ? 1 : 0;
|
|
@@ -2,9 +2,12 @@ import {
|
|
|
2
2
|
defaultCodexConfigPath,
|
|
3
3
|
defaultCodexHooksPath,
|
|
4
4
|
isPlaceholderWebhookUrl
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-TMCNB4JH.js";
|
|
5
|
+
} from "./chunk-376ZI3J7.js";
|
|
7
6
|
import "./chunk-D6JKFJ6A.js";
|
|
7
|
+
import {
|
|
8
|
+
classifyHookCommand,
|
|
9
|
+
isKojeeHookCommand
|
|
10
|
+
} from "./chunk-COM6435C.js";
|
|
8
11
|
import {
|
|
9
12
|
CODEX_STATUS_STALE_MS,
|
|
10
13
|
readAllCodexStatusRaw,
|
|
@@ -16,11 +19,9 @@ import {
|
|
|
16
19
|
} from "./chunk-V5VZPYMZ.js";
|
|
17
20
|
import "./chunk-U5HHHRXA.js";
|
|
18
21
|
import {
|
|
22
|
+
CODEX_LISTEN_CAP_MS,
|
|
19
23
|
VERSION
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import {
|
|
22
|
-
CODEX_LISTEN_CAP_MS
|
|
23
|
-
} from "./chunk-FJUAMJHU.js";
|
|
24
|
+
} from "./chunk-O4JQY7IF.js";
|
|
24
25
|
|
|
25
26
|
// src/doctor-codex.ts
|
|
26
27
|
import fs from "fs";
|
|
@@ -175,6 +176,39 @@ function collectCodexDoctorReport(deps = {}) {
|
|
|
175
176
|
ok: upsPresent ? true : "warn",
|
|
176
177
|
detail: upsPresent ? "present (pending-wake catch-up at the next prompt)" : `MISSING \u2014 a silently-dropped Stop block is never re-surfaced. ${WIZARD_RERUN}`
|
|
177
178
|
});
|
|
179
|
+
const codexHookCommands = [];
|
|
180
|
+
for (const event of ["Stop", "UserPromptSubmit"]) {
|
|
181
|
+
for (const e of hooksJson.hooks?.[event] ?? []) {
|
|
182
|
+
for (const h of e.hooks ?? []) {
|
|
183
|
+
if (typeof h.command === "string" && isKojeeHookCommand(h.command)) codexHookCommands.push(h.command);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (codexHookCommands.length > 0) {
|
|
188
|
+
const states = codexHookCommands.map((c) => classifyHookCommand(c));
|
|
189
|
+
const repin = "fix: `npm i -g kojee-mcp` then re-run connect for codex (re-pins in place, no re-pair)";
|
|
190
|
+
if (states.includes("pinned-broken")) {
|
|
191
|
+
checks.push({
|
|
192
|
+
name: "hook pin (hooks.json)",
|
|
193
|
+
ok: false,
|
|
194
|
+
detail: `pinned cli.js is GONE \u2014 hooks fail with exit 127 RIGHT NOW; ${repin}`
|
|
195
|
+
});
|
|
196
|
+
} else if (states.includes("pinned-ephemeral")) {
|
|
197
|
+
checks.push({
|
|
198
|
+
name: "hook pin (hooks.json)",
|
|
199
|
+
ok: false,
|
|
200
|
+
detail: `pinned INTO the npx cache (~/.npm/_npx/\u2026) \u2014 cache GC will silently kill the hooks (exit 127 every turn); ${repin}`
|
|
201
|
+
});
|
|
202
|
+
} else if (states.includes("npx")) {
|
|
203
|
+
checks.push({
|
|
204
|
+
name: "hook pin (hooks.json)",
|
|
205
|
+
ok: "warn",
|
|
206
|
+
detail: `unpinned (npx -y) \u2014 pays npx resolution every turn and can stall on a registry fetch; ${repin}`
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
checks.push({ name: "hook pin (hooks.json)", ok: true, detail: "pinned to a durable binary" });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
178
212
|
const featureHooks = parseCodexFeatureHooks(toml);
|
|
179
213
|
if (featureHooks !== true) {
|
|
180
214
|
checks.push({
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTandemIds,
|
|
3
3
|
startProxy
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-HRDASFRF.js";
|
|
5
|
+
import "./chunk-XYDNRVOU.js";
|
|
6
6
|
import "./chunk-TBVJOXIR.js";
|
|
7
7
|
import "./chunk-Z5LPNJQ6.js";
|
|
8
8
|
import "./chunk-I67C2HYA.js";
|
|
9
9
|
import "./chunk-MIEI4PLB.js";
|
|
10
10
|
import "./chunk-6G6YYST6.js";
|
|
11
11
|
import "./chunk-U5HHHRXA.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-FJUAMJHU.js";
|
|
12
|
+
import "./chunk-S35ZIVOL.js";
|
|
13
|
+
import "./chunk-O4JQY7IF.js";
|
|
15
14
|
import "./chunk-PPTKGWFF.js";
|
|
16
15
|
import "./chunk-XJEBJIQE.js";
|
|
17
16
|
import "./chunk-SRLD2UY7.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildHookCommand,
|
|
3
|
-
isKojeeHookCommand
|
|
4
|
-
} from "./chunk-TMCNB4JH.js";
|
|
5
1
|
import {
|
|
6
2
|
wrapNpxLauncher
|
|
7
3
|
} from "./chunk-D6JKFJ6A.js";
|
|
4
|
+
import {
|
|
5
|
+
buildHookCommand,
|
|
6
|
+
isKojeeHookCommand
|
|
7
|
+
} from "./chunk-COM6435C.js";
|
|
8
8
|
import {
|
|
9
9
|
secureFile
|
|
10
10
|
} from "./chunk-U5HHHRXA.js";
|
|
@@ -5,20 +5,19 @@ import {
|
|
|
5
5
|
} from "./chunk-XLRF5ATG.js";
|
|
6
6
|
import {
|
|
7
7
|
createCachedOpenclawWakeResolver,
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
postOpenclawAgent
|
|
9
|
+
} from "./chunk-LKGKDZSO.js";
|
|
10
10
|
import "./chunk-PHXO5P25.js";
|
|
11
11
|
import {
|
|
12
12
|
recordDelivered
|
|
13
13
|
} from "./chunk-IZN7IZPW.js";
|
|
14
14
|
import {
|
|
15
15
|
claudeCodeAdapter
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-XYDNRVOU.js";
|
|
17
|
+
import "./chunk-S35ZIVOL.js";
|
|
18
18
|
import {
|
|
19
19
|
VERSION
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import "./chunk-FJUAMJHU.js";
|
|
20
|
+
} from "./chunk-O4JQY7IF.js";
|
|
22
21
|
import "./chunk-PPTKGWFF.js";
|
|
23
22
|
|
|
24
23
|
// src/delivery/lib/fanout.ts
|
|
@@ -127,7 +126,7 @@ function createClaudeCodeDelivery() {
|
|
|
127
126
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
128
127
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
129
128
|
const { startEventStream } = await import("./event-stream-ATKYXDBV.js");
|
|
130
|
-
const { createMcpServer } = await import("./server-
|
|
129
|
+
const { createMcpServer } = await import("./server-RK4UK6SP.js");
|
|
131
130
|
const { deriveDiscoveryKey } = await import("./ancestry-A2F5KQ6A.js");
|
|
132
131
|
const { resolveSharedSessionId } = await import("./cc-session-id-NNCZCHZZ.js");
|
|
133
132
|
sweepStaleDiscovery();
|
|
@@ -324,7 +323,7 @@ function createWebhookDelivery(name, pendingLedger) {
|
|
|
324
323
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
325
324
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
326
325
|
const { startEventStream } = await import("./event-stream-ATKYXDBV.js");
|
|
327
|
-
const { createMcpServer } = await import("./server-
|
|
326
|
+
const { createMcpServer } = await import("./server-RK4UK6SP.js");
|
|
328
327
|
sweepStaleEventLogs();
|
|
329
328
|
eventLog = startEventLog({
|
|
330
329
|
key: ctx.instanceKey,
|
|
@@ -501,14 +500,13 @@ function createCodexDelivery(onDelivered) {
|
|
|
501
500
|
}
|
|
502
501
|
|
|
503
502
|
// src/delivery/openclaw.ts
|
|
504
|
-
function
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
return `New Kojee Tandem activity in ${event.tandem_id} from ${who}: "${body}". Catch up with tandem_messages / tandem_listen and reply via tandem_send.`;
|
|
503
|
+
function formatOpenclawAgentPrompt(event) {
|
|
504
|
+
const since = Math.max(0, (event.cursor ?? 1) - 1);
|
|
505
|
+
return `You were mentioned in Kojee Tandem ${event.tandem_id}. Read it to see who and what \u2014 tandem_messages(tandem_id="${event.tandem_id}", since=${since}) \u2014 then REPLY IN THE FORUM now with tandem_send(tandem_id="${event.tandem_id}", body=..., reply_to="${event.id}"). Do this now; don't just read or ack.`;
|
|
508
506
|
}
|
|
509
507
|
function createOpenclawDelivery(deps = {}) {
|
|
510
508
|
const resolveTarget = deps.resolveTarget ?? createCachedOpenclawWakeResolver();
|
|
511
|
-
const
|
|
509
|
+
const postAgent = deps.postAgent ?? postOpenclawAgent;
|
|
512
510
|
let eventLog = null;
|
|
513
511
|
let streamHandle = null;
|
|
514
512
|
let warnedUnconfigured = false;
|
|
@@ -518,7 +516,7 @@ function createOpenclawDelivery(deps = {}) {
|
|
|
518
516
|
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-2NBJEIEP.js");
|
|
519
517
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
520
518
|
const { startEventStream } = await import("./event-stream-ATKYXDBV.js");
|
|
521
|
-
const { createMcpServer } = await import("./server-
|
|
519
|
+
const { createMcpServer } = await import("./server-RK4UK6SP.js");
|
|
522
520
|
sweepStaleEventLogs();
|
|
523
521
|
eventLog = startEventLog({
|
|
524
522
|
key: ctx.instanceKey,
|
|
@@ -587,8 +585,13 @@ function createOpenclawDelivery(deps = {}) {
|
|
|
587
585
|
return;
|
|
588
586
|
}
|
|
589
587
|
warnedUnconfigured = false;
|
|
590
|
-
const res = await
|
|
591
|
-
|
|
588
|
+
const res = await postAgent(r.target, {
|
|
589
|
+
message: formatOpenclawAgentPrompt(event),
|
|
590
|
+
channel: "last",
|
|
591
|
+
name: "Kojee",
|
|
592
|
+
wakeMode: "now"
|
|
593
|
+
});
|
|
594
|
+
if (!res.ok) console.error(`[openclaw] agent wake POST failed: ${res.error ?? res.status}`);
|
|
592
595
|
})().catch((err) => {
|
|
593
596
|
console.error("[openclaw] wake delivery failed:", err);
|
|
594
597
|
});
|
|
@@ -615,7 +618,7 @@ function selectDelivery(runtime, opts) {
|
|
|
615
618
|
return createCodexDelivery(opts.onDelivered);
|
|
616
619
|
}
|
|
617
620
|
if (runtime === "openclaw") {
|
|
618
|
-
return createOpenclawDelivery();
|
|
621
|
+
return opts.headless ? createOpenclawDelivery() : null;
|
|
619
622
|
}
|
|
620
623
|
if (!needsWebhookEventStream(opts.env)) {
|
|
621
624
|
return null;
|
|
@@ -5,9 +5,8 @@ import {
|
|
|
5
5
|
executeToolCall,
|
|
6
6
|
extractDrainCursor,
|
|
7
7
|
startMcpServer
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-FJUAMJHU.js";
|
|
8
|
+
} from "./chunk-S35ZIVOL.js";
|
|
9
|
+
import "./chunk-O4JQY7IF.js";
|
|
11
10
|
import "./chunk-PPTKGWFF.js";
|
|
12
11
|
export {
|
|
13
12
|
buildChannelInstructions,
|
|
@@ -3,15 +3,15 @@ import {
|
|
|
3
3
|
pairSlotFor,
|
|
4
4
|
reconcileConnectPairSlot,
|
|
5
5
|
runWizard
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-ZUF47CPP.js";
|
|
7
|
+
import "./chunk-376ZI3J7.js";
|
|
8
8
|
import "./chunk-E6WMFMM2.js";
|
|
9
9
|
import "./chunk-GEGUWQYT.js";
|
|
10
10
|
import {
|
|
11
11
|
isWizardRuntime
|
|
12
12
|
} from "./chunk-77HWBSRH.js";
|
|
13
|
-
import "./chunk-TMCNB4JH.js";
|
|
14
13
|
import "./chunk-D6JKFJ6A.js";
|
|
14
|
+
import "./chunk-COM6435C.js";
|
|
15
15
|
import "./chunk-EIH2LNF4.js";
|
|
16
16
|
import "./chunk-RDLF4NQC.js";
|
|
17
17
|
import "./chunk-SQL56SEB.js";
|
|
@@ -26,8 +26,7 @@ import "./chunk-I67C2HYA.js";
|
|
|
26
26
|
import "./chunk-MIEI4PLB.js";
|
|
27
27
|
import "./chunk-6G6YYST6.js";
|
|
28
28
|
import "./chunk-U5HHHRXA.js";
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-FJUAMJHU.js";
|
|
29
|
+
import "./chunk-O4JQY7IF.js";
|
|
31
30
|
|
|
32
31
|
// src/wizard/setup-handler.ts
|
|
33
32
|
var SETUP_SUPPORTED_RUNTIMES = ["claude-code", "codex"];
|
|
@@ -16,6 +16,7 @@ import "./chunk-U5HHHRXA.js";
|
|
|
16
16
|
|
|
17
17
|
// src/tail-stream.ts
|
|
18
18
|
import fs from "fs";
|
|
19
|
+
import { spawn } from "child_process";
|
|
19
20
|
var STATUS_LINE_RE = new RegExp(`^\\[[^\\]]*\\] ${STATUS_LINE_PREFIX} (.*)$`);
|
|
20
21
|
var HEARTBEAT_FIELDS_RE = /(^|\s)status=heartbeat(\s|$)/;
|
|
21
22
|
var ROTATED_FIELDS_RE = /(^|\s)status=rotated(\s|$)/;
|
|
@@ -29,6 +30,7 @@ var TAIL_STALE_FLOOR_MS = envInt(
|
|
|
29
30
|
);
|
|
30
31
|
var STALE_CHECK_INTERVAL_MS = envInt("KOJEE_TAIL_CHECK_MS", 5e3);
|
|
31
32
|
var TAIL_REWARN_AFTER_MS = 30 * 6e4;
|
|
33
|
+
var SUSTAINED_STALE_MS = envInt("KOJEE_TAIL_SUSTAINED_MS", TAIL_REWARN_AFTER_MS);
|
|
32
34
|
var TAIL_HEALTHY_RESET_MS = 30 * 6e4;
|
|
33
35
|
var TAIL_BACKOFF_CAP_CYCLES = 3;
|
|
34
36
|
function envInt(name, fallback) {
|
|
@@ -99,9 +101,13 @@ function createStallDetector(startNow, options = {}) {
|
|
|
99
101
|
}
|
|
100
102
|
};
|
|
101
103
|
}
|
|
102
|
-
async function runTail(messagesPath) {
|
|
104
|
+
async function runTail(messagesPath, opts = {}) {
|
|
105
|
+
const wakeOnly = opts.wakeOnly === true;
|
|
103
106
|
const statusPath = statusLogPath(messagesPath);
|
|
104
|
-
const detector = createStallDetector(Date.now(), {
|
|
107
|
+
const detector = createStallDetector(Date.now(), {
|
|
108
|
+
floorMs: TAIL_STALE_FLOOR_MS,
|
|
109
|
+
rewarnAfterMs: SUSTAINED_STALE_MS
|
|
110
|
+
});
|
|
105
111
|
const heartbeatPath = monitorHeartbeatPath(messagesPath);
|
|
106
112
|
const touchHeartbeat = () => {
|
|
107
113
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -116,7 +122,7 @@ async function runTail(messagesPath) {
|
|
|
116
122
|
};
|
|
117
123
|
let lastCursor = null;
|
|
118
124
|
function onMessageLine(line) {
|
|
119
|
-
if (detector.onLife(Date.now())?.kind === "recovered") {
|
|
125
|
+
if (detector.onLife(Date.now())?.kind === "recovered" && !wakeOnly) {
|
|
120
126
|
process.stdout.write(
|
|
121
127
|
"[kojee] stream recovered \u2014 events flowing again (recovery is automatic; no action needed).\n"
|
|
122
128
|
);
|
|
@@ -133,7 +139,7 @@ async function runTail(messagesPath) {
|
|
|
133
139
|
if (HEARTBEAT_FIELDS_RE.test(fields)) {
|
|
134
140
|
detector.onHeartbeat(now);
|
|
135
141
|
}
|
|
136
|
-
if (detector.onLife(now)?.kind === "recovered") {
|
|
142
|
+
if (detector.onLife(now)?.kind === "recovered" && !wakeOnly) {
|
|
137
143
|
process.stdout.write(
|
|
138
144
|
"[kojee] stream recovered \u2014 heartbeats resumed (the stall was transient, likely idle-period timing or brief host load; no action needed).\n"
|
|
139
145
|
);
|
|
@@ -149,7 +155,12 @@ async function runTail(messagesPath) {
|
|
|
149
155
|
}
|
|
150
156
|
}
|
|
151
157
|
const followers = [
|
|
152
|
-
|
|
158
|
+
// MESSAGES (wake-critical) — prefer the OS `tail -F` follower where the
|
|
159
|
+
// platform has one; it delegates file-following to the battle-tested system
|
|
160
|
+
// binary instead of the Node fs.watch/poll tailer. STATUS (telemetry) stays
|
|
161
|
+
// on the Node follower: its startup-backlog seeding boundary (statusSeeded)
|
|
162
|
+
// is clean to detect only when we control the drain. See makeMessagesFollower.
|
|
163
|
+
makeMessagesFollower(messagesPath, onMessageLine, touchHeartbeat),
|
|
153
164
|
makeFollower(statusPath, onStatusLine, touchHeartbeat)
|
|
154
165
|
];
|
|
155
166
|
void Promise.all([
|
|
@@ -171,6 +182,7 @@ async function runTail(messagesPath) {
|
|
|
171
182
|
);
|
|
172
183
|
return;
|
|
173
184
|
}
|
|
185
|
+
if (wakeOnly) return;
|
|
174
186
|
const thresholdS = Math.round((signal.thresholdMs ?? 0) / 1e3);
|
|
175
187
|
process.stdout.write(
|
|
176
188
|
`[kojee] WARNING: no events or heartbeat for ${silentS}s (threshold ${thresholdS}s) \u2014 stream may be dead, but this is often benign (idle stream, or brief host load delaying heartbeats) and recovery is automatic; if it persists, run \`npx kojee-mcp doctor\`.
|
|
@@ -273,11 +285,99 @@ function makeFollower(filePath, onLine, onActivity) {
|
|
|
273
285
|
}
|
|
274
286
|
};
|
|
275
287
|
}
|
|
288
|
+
function osTailAvailable() {
|
|
289
|
+
const override = (process.env.KOJEE_TAIL_IMPL ?? "").trim().toLowerCase();
|
|
290
|
+
if (override === "os") return true;
|
|
291
|
+
if (override === "node") return false;
|
|
292
|
+
return process.platform !== "win32";
|
|
293
|
+
}
|
|
294
|
+
function makeMessagesFollower(filePath, onLine, onActivity) {
|
|
295
|
+
if (!osTailAvailable()) return makeFollower(filePath, onLine, onActivity);
|
|
296
|
+
return makeTailFollower(
|
|
297
|
+
filePath,
|
|
298
|
+
onLine,
|
|
299
|
+
onActivity,
|
|
300
|
+
() => makeFollower(filePath, onLine, onActivity)
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
function makeTailFollower(filePath, onLine, onActivity, fallback) {
|
|
304
|
+
let child = null;
|
|
305
|
+
let heir = null;
|
|
306
|
+
let hbTimer = null;
|
|
307
|
+
let buffer = "";
|
|
308
|
+
let stopped = false;
|
|
309
|
+
let handedOff = false;
|
|
310
|
+
function emit(chunk) {
|
|
311
|
+
onActivity();
|
|
312
|
+
buffer += chunk;
|
|
313
|
+
let nl;
|
|
314
|
+
while ((nl = buffer.indexOf("\n")) !== -1) {
|
|
315
|
+
onLine(buffer.slice(0, nl));
|
|
316
|
+
buffer = buffer.slice(nl + 1);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function handOff() {
|
|
320
|
+
if (stopped || handedOff) return;
|
|
321
|
+
handedOff = true;
|
|
322
|
+
if (hbTimer) {
|
|
323
|
+
clearInterval(hbTimer);
|
|
324
|
+
hbTimer = null;
|
|
325
|
+
}
|
|
326
|
+
if (child) {
|
|
327
|
+
try {
|
|
328
|
+
child.kill("SIGTERM");
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
child = null;
|
|
332
|
+
}
|
|
333
|
+
heir = fallback();
|
|
334
|
+
void heir.start().catch(() => {
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
async start() {
|
|
339
|
+
try {
|
|
340
|
+
child = spawn("tail", ["-n", "+1", "-F", filePath], {
|
|
341
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
342
|
+
});
|
|
343
|
+
} catch {
|
|
344
|
+
handOff();
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
child.on("error", () => handOff());
|
|
348
|
+
child.on("exit", () => {
|
|
349
|
+
if (!stopped) handOff();
|
|
350
|
+
});
|
|
351
|
+
child.stdout?.setEncoding("utf8");
|
|
352
|
+
child.stdout?.on("data", (d) => emit(d));
|
|
353
|
+
onActivity();
|
|
354
|
+
hbTimer = setInterval(onActivity, 1e3);
|
|
355
|
+
},
|
|
356
|
+
stop() {
|
|
357
|
+
stopped = true;
|
|
358
|
+
if (hbTimer) {
|
|
359
|
+
clearInterval(hbTimer);
|
|
360
|
+
hbTimer = null;
|
|
361
|
+
}
|
|
362
|
+
if (child) {
|
|
363
|
+
try {
|
|
364
|
+
child.kill("SIGTERM");
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
child = null;
|
|
368
|
+
}
|
|
369
|
+
if (heir) heir.stop();
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
276
373
|
function sleep(ms) {
|
|
277
374
|
return new Promise((r) => setTimeout(r, ms));
|
|
278
375
|
}
|
|
279
376
|
export {
|
|
280
377
|
createStallDetector,
|
|
281
378
|
makeFollower,
|
|
379
|
+
makeMessagesFollower,
|
|
380
|
+
makeTailFollower,
|
|
381
|
+
osTailAvailable,
|
|
282
382
|
runTail
|
|
283
383
|
};
|
package/package.json
CHANGED
package/dist/chunk-5DHIUN73.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// src/version.ts
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
|
-
var FALLBACK_VERSION = "0.0.0-unknown";
|
|
6
|
-
function resolveVersion() {
|
|
7
|
-
try {
|
|
8
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const parsed = JSON.parse(
|
|
10
|
-
fs.readFileSync(path.join(here, "..", "package.json"), "utf8")
|
|
11
|
-
);
|
|
12
|
-
return typeof parsed?.version === "string" && parsed.version ? parsed.version : FALLBACK_VERSION;
|
|
13
|
-
} catch (err) {
|
|
14
|
-
process.stderr.write(
|
|
15
|
-
`kojee-mcp: could not resolve version from package.json, falling back to ${FALLBACK_VERSION}: ${String(err)}
|
|
16
|
-
`
|
|
17
|
-
);
|
|
18
|
-
return FALLBACK_VERSION;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
var VERSION = resolveVersion();
|
|
22
|
-
|
|
23
|
-
export {
|
|
24
|
-
VERSION
|
|
25
|
-
};
|
package/dist/chunk-TMCNB4JH.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// src/hooks/hook-command.ts
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
|
-
function npxHookCommand(type) {
|
|
6
|
-
return `npx -y kojee-mcp hook --type=${type}`;
|
|
7
|
-
}
|
|
8
|
-
function resolveCliEntry(moduleUrl = import.meta.url) {
|
|
9
|
-
try {
|
|
10
|
-
const here = fileURLToPath(moduleUrl);
|
|
11
|
-
const candidate = path.join(path.dirname(here), "cli.js");
|
|
12
|
-
return fs.existsSync(candidate) ? candidate : null;
|
|
13
|
-
} catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function buildHookCommand(type, opts = {}) {
|
|
18
|
-
const cliEntry = opts.cliEntry !== void 0 ? opts.cliEntry : resolveCliEntry();
|
|
19
|
-
if (!cliEntry) return npxHookCommand(type);
|
|
20
|
-
const execPath = opts.execPath ?? process.execPath;
|
|
21
|
-
return `"${execPath}" "${cliEntry}" hook --type=${type}`;
|
|
22
|
-
}
|
|
23
|
-
var PINNED_HOOK_RE = /^"[^"]+" "[^"]+" hook --type=(stop|user-prompt-submit|codex-stop|codex-prompt-submit)$/;
|
|
24
|
-
function isKojeeHookCommand(command) {
|
|
25
|
-
if (command.startsWith("npx -y kojee-mcp hook --type=")) return true;
|
|
26
|
-
return PINNED_HOOK_RE.test(command);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export {
|
|
30
|
-
npxHookCommand,
|
|
31
|
-
buildHookCommand,
|
|
32
|
-
isKojeeHookCommand
|
|
33
|
-
};
|