kojee-mcp 0.6.0 → 0.7.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.
- package/README.md +13 -8
- package/dist/ancestry-A2F5KQ6A.js +10 -0
- package/dist/cc-session-id-RURNIHHC.js +10 -0
- package/dist/{chunk-KH5YEJIM.js → chunk-247WFMCJ.js} +7 -20
- package/dist/{chunk-BYV7CWNY.js → chunk-5H75AEZ2.js} +22 -9
- package/dist/{chunk-YH27B6SW.js → chunk-5SZHXYPK.js} +16 -3
- package/dist/{chunk-DO42NPNR.js → chunk-67F67AQ6.js} +14 -3
- package/dist/{chunk-CH32ELFX.js → chunk-6G6YYST6.js} +1 -1
- package/dist/chunk-D6JKFJ6A.js +11 -0
- package/dist/chunk-E6WMFMM2.js +100 -0
- package/dist/chunk-EIAUW6KO.js +82 -0
- package/dist/chunk-EIH2LNF4.js +45 -0
- package/dist/{chunk-XN3IC4B5.js → chunk-FSKGQ6GT.js} +30 -5
- package/dist/{chunk-GI2CKKBL.js → chunk-GKXHKR3B.js} +1 -1
- package/dist/{setup-handler-7HOFAD7C.js → chunk-GVWYW7MY.js} +247 -246
- package/dist/{chunk-JXMVZEQ7.js → chunk-I67C2HYA.js} +1 -1
- package/dist/chunk-KNEJTD6G.js +31 -0
- package/dist/{chunk-NR4Y54OL.js → chunk-MIEI4PLB.js} +1 -19
- package/dist/chunk-NE2F5CKS.js +113 -0
- package/dist/{chunk-N35X22G7.js → chunk-OB5T6P24.js} +1 -1
- package/dist/{chunk-65KRRDHP.js → chunk-OBIJ6WQP.js} +82 -27
- package/dist/{chunk-Q7T3WAST.js → chunk-QEJUNP3X.js} +2 -1
- package/dist/chunk-QJFMU4QC.js +33 -0
- package/dist/{chunk-EW72ZNQL.js → chunk-RDLF4NQC.js} +1 -1
- package/dist/{chunk-ALZX3SGI.js → chunk-SGRVG4HW.js} +8 -3
- package/dist/{chunk-DURRQDYI.js → chunk-TNZITJAB.js} +45 -15
- package/dist/chunk-U5HHHRXA.js +103 -0
- package/dist/chunk-WBXU27BF.js +35 -0
- package/dist/chunk-WIU5WGDF.js +10 -0
- package/dist/chunk-XJEBJIQE.js +52 -0
- package/dist/chunk-Z5LPNJQ6.js +20 -0
- package/dist/cli.js +62 -25
- package/dist/{chunk-KFDZWNQO.js → codex-stop-hook-EOMQD3J4.js} +11 -15
- package/dist/connect-handler-HIRM624N.js +137 -0
- package/dist/{control-token-4BUCTYQB.js → control-token-KXSZ3GBY.js} +2 -2
- package/dist/{doctor-WVX3WCYX.js → doctor-PLKLHTJM.js} +48 -22
- package/dist/{doctor-codex-Z5M4SFBG.js → doctor-codex-HARAAH4Y.js} +23 -6
- package/dist/doctor-openclaw-LL6NSPLC.js +86 -0
- package/dist/{event-log-HOBO3ECB.js → event-log-2NBJEIEP.js} +5 -3
- package/dist/{event-stream-SYBUJ5EH.js → event-stream-KRYWEYWO.js} +7 -4
- package/dist/index.js +12 -10
- package/dist/{install-LJY2CHKG.js → install-V7LSQCYZ.js} +37 -22
- package/dist/{keystore-XLEV3FL5.js → keystore-LQNFDHGO.js} +2 -2
- package/dist/lib.d.ts +4 -1
- package/dist/lib.js +11 -8
- package/dist/pair-HTCVSMR6.js +11 -0
- package/dist/{paired-config-JTFLHMZ2.js → paired-config-GUDDO2CH.js} +4 -2
- package/dist/{parent-watchdog-TLU355FB.js → parent-watchdog-LTUNF7PD.js} +24 -5
- package/dist/{registry-YLOFZDI2.js → registry-VRFHKOPP.js} +193 -24
- package/dist/resolve-slot-BFIB46Y5.js +13 -0
- package/dist/{runtime-record-WO4IECM6.js → runtime-record-OXRLTOLC.js} +2 -2
- package/dist/{send-cli-IYJF32PE.js → send-cli-45RLGYIC.js} +22 -10
- package/dist/{server-R24AVNTI.js → server-YTHOMA4H.js} +4 -2
- package/dist/{session-discovery-FNMJGFPM.js → session-discovery-NHCZ2XWA.js} +2 -2
- package/dist/setup-handler-UHVKSX7E.js +159 -0
- package/dist/{stop-hook-TCPJFGFF.js → stop-hook-YI4KRKF2.js} +17 -20
- package/dist/{tail-stream-4IMXI44S.js → tail-stream-NBGHHBS4.js} +40 -22
- package/dist/{user-prompt-submit-hook-FQ43NIHC.js → user-prompt-submit-hook-DTXXFDSD.js} +11 -14
- package/package.json +1 -1
- package/dist/ancestry-ONFBQEP5.js +0 -8
- package/dist/chunk-BLEGIR35.js +0 -43
- package/dist/chunk-CLKCNV2A.js +0 -100
- package/dist/chunk-OGHDTFAX.js +0 -50
- package/dist/chunk-VHKPWUX7.js +0 -41
- package/dist/codex-stop-hook-RWQSZTST.js +0 -16
- package/dist/doctor-openclaw-SS2TMQOX.js +0 -95
- package/dist/pair-P4ILCMT7.js +0 -10
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// src/fs-retry.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
var RETRYABLE = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
4
|
+
function sleepSync(ms) {
|
|
5
|
+
if (ms <= 0) return;
|
|
6
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
7
|
+
}
|
|
8
|
+
function withFileOpRetry(op, opts = {}) {
|
|
9
|
+
const tries = opts.tries ?? 5;
|
|
10
|
+
const backoffMs = opts.backoffMs ?? 15;
|
|
11
|
+
let lastErr;
|
|
12
|
+
for (let i = 0; i < tries; i++) {
|
|
13
|
+
try {
|
|
14
|
+
return op();
|
|
15
|
+
} catch (err) {
|
|
16
|
+
const code = err.code;
|
|
17
|
+
if (!code || !RETRYABLE.has(code)) throw err;
|
|
18
|
+
lastErr = err;
|
|
19
|
+
if (i < tries - 1) sleepSync(backoffMs);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
throw lastErr;
|
|
23
|
+
}
|
|
24
|
+
function renameSyncRetry(from, to, opts) {
|
|
25
|
+
withFileOpRetry(() => fs.renameSync(from, to), opts);
|
|
26
|
+
}
|
|
27
|
+
function unlinkSyncRetry(p, opts) {
|
|
28
|
+
try {
|
|
29
|
+
withFileOpRetry(() => fs.unlinkSync(p), opts);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (err.code !== "ENOENT") throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/secure-file.ts
|
|
36
|
+
import fs2 from "fs";
|
|
37
|
+
import os from "os";
|
|
38
|
+
import { execFileSync } from "child_process";
|
|
39
|
+
function isWindows() {
|
|
40
|
+
return process.platform === "win32";
|
|
41
|
+
}
|
|
42
|
+
function secureFile(filePath) {
|
|
43
|
+
if (isWindows()) {
|
|
44
|
+
restrictAclToCurrentUser(filePath, "(F)");
|
|
45
|
+
} else {
|
|
46
|
+
try {
|
|
47
|
+
fs2.chmodSync(filePath, 384);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function secureDir(dirPath) {
|
|
53
|
+
if (isWindows()) {
|
|
54
|
+
restrictAclToCurrentUser(dirPath, "(OI)(CI)(F)");
|
|
55
|
+
} else {
|
|
56
|
+
try {
|
|
57
|
+
fs2.chmodSync(dirPath, 448);
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function currentUserSid() {
|
|
63
|
+
try {
|
|
64
|
+
const out = execFileSync("whoami", ["/user", "/fo", "csv", "/nh"], { encoding: "utf8" });
|
|
65
|
+
const m = out.match(/S-1-[0-9-]+/);
|
|
66
|
+
return m ? m[0] : null;
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function restrictAclToCurrentUser(target, permission) {
|
|
72
|
+
try {
|
|
73
|
+
const sid = currentUserSid();
|
|
74
|
+
const grantee = sid ? `*${sid}` : os.userInfo().username;
|
|
75
|
+
execFileSync("icacls", [target, "/grant:r", `${grantee}:${permission}`], { stdio: "ignore" });
|
|
76
|
+
execFileSync("icacls", [target, "/inheritance:r"], { stdio: "ignore" });
|
|
77
|
+
if (permission.includes("(F)")) {
|
|
78
|
+
try {
|
|
79
|
+
fs2.accessSync(target, fs2.constants.R_OK);
|
|
80
|
+
} catch {
|
|
81
|
+
try {
|
|
82
|
+
execFileSync("icacls", [target, "/grant:r", `${grantee}:${permission}`], { stdio: "ignore" });
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function createSecureFileSync(filePath) {
|
|
91
|
+
unlinkSyncRetry(filePath);
|
|
92
|
+
const fd = fs2.openSync(filePath, "wx", 384);
|
|
93
|
+
fs2.closeSync(fd);
|
|
94
|
+
secureFile(filePath);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export {
|
|
98
|
+
renameSyncRetry,
|
|
99
|
+
unlinkSyncRetry,
|
|
100
|
+
secureFile,
|
|
101
|
+
secureDir,
|
|
102
|
+
createSecureFileSync
|
|
103
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readSessionDiscoveryByKey
|
|
3
|
+
} from "./chunk-67F67AQ6.js";
|
|
4
|
+
import {
|
|
5
|
+
deriveDiscoveryKey,
|
|
6
|
+
findClaudeAncestorPid
|
|
7
|
+
} from "./chunk-XJEBJIQE.js";
|
|
8
|
+
import {
|
|
9
|
+
resolveSharedSessionId
|
|
10
|
+
} from "./chunk-KNEJTD6G.js";
|
|
11
|
+
|
|
12
|
+
// src/hooks/discovery.ts
|
|
13
|
+
async function resolveHookDiscovery(stdinSessionId, deps = {}) {
|
|
14
|
+
const read = deps.readDiscovery ?? readSessionDiscoveryByKey;
|
|
15
|
+
const findPid = deps.findAncestorPid ?? findClaudeAncestorPid;
|
|
16
|
+
const projectDir = deps.projectDir !== void 0 ? deps.projectDir : process.env["CLAUDE_PROJECT_DIR"];
|
|
17
|
+
const sessionId = resolveSharedSessionId({ env: {}, stdinSessionId });
|
|
18
|
+
if (sessionId) {
|
|
19
|
+
const sidKey = deriveDiscoveryKey(projectDir, null, sessionId);
|
|
20
|
+
const sidHit = read(sidKey);
|
|
21
|
+
if (sidHit) return { discovery: sidHit, key: sidKey };
|
|
22
|
+
const ccPid2 = await findPid();
|
|
23
|
+
const legacyKey2 = deriveDiscoveryKey(projectDir, ccPid2, null);
|
|
24
|
+
const legacyHit = read(legacyKey2);
|
|
25
|
+
if (legacyHit) return { discovery: legacyHit, key: legacyKey2 };
|
|
26
|
+
return { discovery: null, key: sidKey };
|
|
27
|
+
}
|
|
28
|
+
const ccPid = await findPid();
|
|
29
|
+
const legacyKey = deriveDiscoveryKey(projectDir, ccPid, null);
|
|
30
|
+
return { discovery: read(legacyKey), key: legacyKey };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
resolveHookDiscovery
|
|
35
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/runtime/ancestry.ts
|
|
2
|
+
import psList from "ps-list";
|
|
3
|
+
import { createHash } from "crypto";
|
|
4
|
+
var CLAUDE_PROCESS_RE = /\bclaude\b/i;
|
|
5
|
+
function rowLooksLikeClaude(row) {
|
|
6
|
+
return CLAUDE_PROCESS_RE.test(`${row.name} ${row.cmd ?? ""}`);
|
|
7
|
+
}
|
|
8
|
+
async function findClaudeAncestorPid(startPid = process.ppid) {
|
|
9
|
+
let processes;
|
|
10
|
+
try {
|
|
11
|
+
processes = await psList();
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const byPid = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const p of processes) byPid.set(p.pid, p);
|
|
17
|
+
let pid = startPid;
|
|
18
|
+
for (let depth = 0; depth < 20 && pid !== void 0 && pid > 1; depth++) {
|
|
19
|
+
const row = byPid.get(pid);
|
|
20
|
+
if (!row) return null;
|
|
21
|
+
if (rowLooksLikeClaude(row)) return pid;
|
|
22
|
+
if (row.ppid === void 0 || row.ppid === pid) return null;
|
|
23
|
+
pid = row.ppid;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
async function pidLooksLikeClaude(pid) {
|
|
28
|
+
let processes;
|
|
29
|
+
try {
|
|
30
|
+
processes = await psList();
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const row = processes.find((p) => p.pid === pid);
|
|
35
|
+
if (!row) return null;
|
|
36
|
+
return rowLooksLikeClaude(row);
|
|
37
|
+
}
|
|
38
|
+
function deriveDiscoveryKey(projectDir, ccPid, sessionId) {
|
|
39
|
+
const hasProjectDir = typeof projectDir === "string" && projectDir.length > 0;
|
|
40
|
+
const prefix = createHash("sha256").update(hasProjectDir ? projectDir : "<no-project>").digest("hex").slice(0, 12);
|
|
41
|
+
const sid = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
42
|
+
if (sid) return `${prefix}-s-${sid}`;
|
|
43
|
+
if (!hasProjectDir && ccPid === null) return `orphan-${process.pid}`;
|
|
44
|
+
if (ccPid === null) return `${prefix}-orphan`;
|
|
45
|
+
return `${prefix}-${ccPid}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export {
|
|
49
|
+
findClaudeAncestorPid,
|
|
50
|
+
pidLooksLikeClaude,
|
|
51
|
+
deriveDiscoveryKey
|
|
52
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/tandem/session-id.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { ulid } from "ulidx";
|
|
4
|
+
var MCP_SESSION_ID = ulid();
|
|
5
|
+
function deriveStableSessionId(token, instanceKey) {
|
|
6
|
+
return crypto.createHash("sha256").update(`${token}:${instanceKey}`).digest("hex").slice(0, 32);
|
|
7
|
+
}
|
|
8
|
+
var _stableSessionId = null;
|
|
9
|
+
function setStableSessionId(id) {
|
|
10
|
+
_stableSessionId = id;
|
|
11
|
+
}
|
|
12
|
+
function getSessionId() {
|
|
13
|
+
return _stableSessionId ?? MCP_SESSION_ID;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
deriveStableSessionId,
|
|
18
|
+
setStableSessionId,
|
|
19
|
+
getSessionId
|
|
20
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runPair
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EIAUW6KO.js";
|
|
5
5
|
import {
|
|
6
6
|
startProxy
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-5H75AEZ2.js";
|
|
8
|
+
import "./chunk-OB5T6P24.js";
|
|
9
9
|
import {
|
|
10
10
|
pairedConfigPath
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-5SZHXYPK.js";
|
|
12
|
+
import "./chunk-247WFMCJ.js";
|
|
13
|
+
import "./chunk-I67C2HYA.js";
|
|
14
|
+
import "./chunk-Z5LPNJQ6.js";
|
|
15
|
+
import "./chunk-MIEI4PLB.js";
|
|
15
16
|
import {
|
|
16
17
|
defaultPairedKeystorePath,
|
|
17
18
|
deriveKeystorePath
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-6G6YYST6.js";
|
|
20
|
+
import "./chunk-U5HHHRXA.js";
|
|
20
21
|
import {
|
|
21
22
|
VERSION
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-SGRVG4HW.js";
|
|
24
|
+
import "./chunk-QEJUNP3X.js";
|
|
24
25
|
import "./chunk-PPTKGWFF.js";
|
|
25
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-XJEBJIQE.js";
|
|
27
|
+
import "./chunk-KNEJTD6G.js";
|
|
26
28
|
|
|
27
29
|
// src/cli.ts
|
|
28
30
|
import { Command } from "commander";
|
|
@@ -42,17 +44,30 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
42
44
|
process.exit(1);
|
|
43
45
|
}
|
|
44
46
|
});
|
|
47
|
+
program.command("connect <code>").description(
|
|
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."
|
|
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) => {
|
|
50
|
+
const { runConnect } = await import("./connect-handler-HIRM624N.js");
|
|
51
|
+
const result = await runConnect({
|
|
52
|
+
code,
|
|
53
|
+
runtime: opts.runtime,
|
|
54
|
+
...opts.url ? { url: opts.url } : {}
|
|
55
|
+
});
|
|
56
|
+
for (const m of result.messages) console.error(m);
|
|
57
|
+
console.error(result.output);
|
|
58
|
+
process.exit(result.exitCode);
|
|
59
|
+
});
|
|
45
60
|
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) => {
|
|
46
61
|
if (opts.type === "stop") {
|
|
47
|
-
const { runStopHook } = await import("./stop-hook-
|
|
62
|
+
const { runStopHook } = await import("./stop-hook-YI4KRKF2.js");
|
|
48
63
|
await runStopHook();
|
|
49
64
|
process.exit(0);
|
|
50
65
|
} else if (opts.type === "user-prompt-submit") {
|
|
51
|
-
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-
|
|
66
|
+
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-DTXXFDSD.js");
|
|
52
67
|
await runUserPromptSubmitHook();
|
|
53
68
|
process.exit(0);
|
|
54
69
|
} else if (opts.type === "codex-stop") {
|
|
55
|
-
const { runCodexStopHook } = await import("./codex-stop-hook-
|
|
70
|
+
const { runCodexStopHook } = await import("./codex-stop-hook-EOMQD3J4.js");
|
|
56
71
|
await runCodexStopHook();
|
|
57
72
|
process.exit(0);
|
|
58
73
|
} else {
|
|
@@ -61,16 +76,17 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
|
|
|
61
76
|
}
|
|
62
77
|
});
|
|
63
78
|
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) => {
|
|
64
|
-
const { installHooks, uninstallHooks } = await import("./install-
|
|
79
|
+
const { installHooks, uninstallHooks } = await import("./install-V7LSQCYZ.js");
|
|
65
80
|
if (opts.uninstall) {
|
|
66
81
|
const removed = uninstallHooks({ hooksPath: opts.hooksPath });
|
|
67
82
|
console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
|
|
68
83
|
} else {
|
|
69
84
|
const { stop, ups } = installHooks({ hooksPath: opts.hooksPath });
|
|
85
|
+
const label = (s) => s === "added" ? "\u2713 added" : s === "updated" ? "\u21BB updated" : "\u21BB already installed";
|
|
70
86
|
console.error(
|
|
71
87
|
"Installed kojee hooks in " + (opts.hooksPath ?? "~/.claude/settings.json") + `:
|
|
72
|
-
- Stop: ${stop
|
|
73
|
-
- UserPromptSubmit: ${ups
|
|
88
|
+
- Stop: ${label(stop)}
|
|
89
|
+
- UserPromptSubmit: ${label(ups)}
|
|
74
90
|
Restart Claude Code for hooks to take effect.`
|
|
75
91
|
);
|
|
76
92
|
}
|
|
@@ -78,7 +94,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
78
94
|
program.command("send <tandem_id>").description(
|
|
79
95
|
"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)."
|
|
80
96
|
).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) => {
|
|
81
|
-
const { runSendCli } = await import("./send-cli-
|
|
97
|
+
const { runSendCli } = await import("./send-cli-45RLGYIC.js");
|
|
82
98
|
const { exitCode, envelope } = await runSendCli({
|
|
83
99
|
tandemId,
|
|
84
100
|
body: opts.body,
|
|
@@ -89,7 +105,7 @@ program.command("send <tandem_id>").description(
|
|
|
89
105
|
process.exit(exitCode);
|
|
90
106
|
});
|
|
91
107
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
92
|
-
const { runTail } = await import("./tail-stream-
|
|
108
|
+
const { runTail } = await import("./tail-stream-NBGHHBS4.js");
|
|
93
109
|
try {
|
|
94
110
|
await runTail(filePath);
|
|
95
111
|
} catch (err) {
|
|
@@ -98,12 +114,12 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
98
114
|
}
|
|
99
115
|
});
|
|
100
116
|
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 () => {
|
|
101
|
-
const { runDoctor } = await import("./doctor-
|
|
117
|
+
const { runDoctor } = await import("./doctor-PLKLHTJM.js");
|
|
102
118
|
const code = await runDoctor();
|
|
103
119
|
process.exit(code);
|
|
104
120
|
});
|
|
105
121
|
function addInstallOptions(cmd, runtimeHelp) {
|
|
106
|
-
return cmd.option("--runtime <id>", runtimeHelp).option("--config-path <path>", "Override the runtime's MCP-config path").option("--hooks-path <path>", "Override the runtime's hooks-file path").option("--token <token>", "Gateway token \u2014 token mode (writes --token/--url into the MCP config; requires --url)").option("--url <url>", "Broker base URL (token mode / pair URL; default https://rosie-staging.kojee.net)").option("--pair <code>", "Pair code \u2014 runs the pair flow before configuring (alias of --pair-code)").option("--pair-code <code>", "Pair code \u2014 runs the pair flow (
|
|
122
|
+
return cmd.option("--runtime <id>", runtimeHelp).option("--config-path <path>", "Override the runtime's MCP-config path").option("--hooks-path <path>", "Override the runtime's hooks-file path").option("--token <token>", "Gateway token \u2014 token mode (writes --token/--url into the MCP config; requires --url)").option("--url <url>", "Broker base URL (token mode / pair URL; default https://rosie-staging.kojee.net)").option("--pair <code>", "Pair code \u2014 runs the pair flow before configuring (alias of --pair-code)").option("--pair-code <code>", "Pair code \u2014 runs the pair flow before configuring (connect runtimes write ~/.kojee/agents/<runtime>/config.json; hermes/openclaw write ~/.kojee/config.json)").option("--webhook-url <url>", "Webhook receiver URL (codex/hermes/openclaw)").option("--webhook-secret <secret>", "Webhook HMAC secret (generated if omitted)").option(
|
|
107
123
|
"--webhook-signature-format <preset>",
|
|
108
124
|
'Signature preset: "github" = header X-Hub-Signature-256, value sha256=<hex> (default: bare hex in X-Kojee-Signature)'
|
|
109
125
|
).option(
|
|
@@ -117,7 +133,7 @@ function addInstallOptions(cmd, runtimeHelp) {
|
|
|
117
133
|
function makeInstallAction(verb) {
|
|
118
134
|
return async (opts) => {
|
|
119
135
|
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
120
|
-
const { runSetup, resolvePairCode } = await import("./setup-handler-
|
|
136
|
+
const { runSetup, resolvePairCode } = await import("./setup-handler-UHVKSX7E.js");
|
|
121
137
|
const pairCode = resolvePairCode(opts);
|
|
122
138
|
const result = await runSetup({
|
|
123
139
|
verb,
|
|
@@ -203,6 +219,9 @@ async function promptWebhookUrlFromTty() {
|
|
|
203
219
|
return withReadline((ask) => ask("Webhook receiver URL (press Enter to skip and set it up later):\n> "));
|
|
204
220
|
}
|
|
205
221
|
program.option("--token <token>", "Gateway token (for token-mode)").option("--url <url>", "Broker base URL (for token-mode; required if --token is passed)").option(
|
|
222
|
+
"--paired-config <path>",
|
|
223
|
+
"Explicit per-runtime paired config path (written by `connect`)"
|
|
224
|
+
).option(
|
|
206
225
|
"--keystore-path <path>",
|
|
207
226
|
"Path to keypair file (defaults to per-token path under ~/.kojee/)"
|
|
208
227
|
).action(async (opts) => {
|
|
@@ -211,18 +230,36 @@ program.option("--token <token>", "Gateway token (for token-mode)").option("--ur
|
|
|
211
230
|
let keystorePath = opts.keystorePath;
|
|
212
231
|
const authMode = token ? "token" : "paired";
|
|
213
232
|
if (token) {
|
|
233
|
+
if (opts.pairedConfig) {
|
|
234
|
+
console.error(
|
|
235
|
+
"[kojee-mcp] both --token and --paired-config given; using --token (the per-runtime paired config is ignored)."
|
|
236
|
+
);
|
|
237
|
+
}
|
|
214
238
|
if (!url) {
|
|
215
239
|
console.error("--url is required when --token is provided");
|
|
216
240
|
process.exit(1);
|
|
217
241
|
}
|
|
218
242
|
url = url.replace(/\/+$/, "");
|
|
219
243
|
keystorePath ??= deriveKeystorePath(token);
|
|
244
|
+
} else if (opts.pairedConfig) {
|
|
245
|
+
const { resolveLaunchCreds } = await import("./resolve-slot-BFIB46Y5.js");
|
|
246
|
+
const resolved = resolveLaunchCreds({
|
|
247
|
+
pairedConfig: opts.pairedConfig,
|
|
248
|
+
...url ? { url } : {}
|
|
249
|
+
});
|
|
250
|
+
if (!resolved.ok) {
|
|
251
|
+
console.error(resolved.error);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
token = resolved.token;
|
|
255
|
+
url = resolved.url;
|
|
256
|
+
keystorePath = resolved.keystorePath;
|
|
220
257
|
} else {
|
|
221
|
-
const { loadPairedConfig } = await import("./paired-config-
|
|
258
|
+
const { loadPairedConfig } = await import("./paired-config-GUDDO2CH.js");
|
|
222
259
|
const cfg = loadPairedConfig();
|
|
223
260
|
if (!cfg) {
|
|
224
261
|
console.error(
|
|
225
|
-
"No --token provided and no ~/.kojee/config.json found. Run `kojee-mcp
|
|
262
|
+
"No --token provided and no ~/.kojee/config.json found. Run `kojee-mcp connect <code> --runtime <id>` (or `kojee-mcp pair`) first."
|
|
226
263
|
);
|
|
227
264
|
process.exit(1);
|
|
228
265
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readHookStdin
|
|
3
3
|
} from "./chunk-LSUB6QMP.js";
|
|
4
|
+
import {
|
|
5
|
+
codexPendingMarkerPath
|
|
6
|
+
} from "./chunk-WIU5WGDF.js";
|
|
4
7
|
import {
|
|
5
8
|
buildCodexWakeReason
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QEJUNP3X.js";
|
|
7
10
|
|
|
8
11
|
// src/hooks/codex-stop-hook.ts
|
|
9
12
|
import fs from "fs";
|
|
10
|
-
import os from "os";
|
|
11
|
-
import path from "path";
|
|
12
13
|
var CODEX_PEEK_MS = clampPeekMs(
|
|
13
14
|
Number.parseInt(process.env["KOJEE_CODEX_PEEK_MS"] ?? "150", 10)
|
|
14
15
|
);
|
|
@@ -26,12 +27,8 @@ function decideCodexStopHook(deps) {
|
|
|
26
27
|
reason: buildCodexWakeReason(peek.cursor)
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
return path.join(os.homedir(), ".kojee", `codex-pending-${safe}`);
|
|
32
|
-
}
|
|
33
|
-
function defaultPeekPending(sessionId) {
|
|
34
|
-
const markerPath = codexPendingMarkerPath(sessionId);
|
|
30
|
+
function defaultPeekPending() {
|
|
31
|
+
const markerPath = codexPendingMarkerPath();
|
|
35
32
|
try {
|
|
36
33
|
const st = fs.statSync(markerPath);
|
|
37
34
|
const ageMs = Date.now() - st.mtimeMs;
|
|
@@ -49,12 +46,12 @@ function defaultPeekPending(sessionId) {
|
|
|
49
46
|
}
|
|
50
47
|
}
|
|
51
48
|
async function runCodexStopHook() {
|
|
52
|
-
const {
|
|
49
|
+
const { stopHookActive } = await readHookStdin();
|
|
53
50
|
const out = decideCodexStopHook({
|
|
54
51
|
stopHookActive,
|
|
55
52
|
peekPending: () => {
|
|
56
53
|
try {
|
|
57
|
-
return defaultPeekPending(
|
|
54
|
+
return defaultPeekPending();
|
|
58
55
|
} catch {
|
|
59
56
|
return { pending: false, cursor: null };
|
|
60
57
|
}
|
|
@@ -63,11 +60,10 @@ async function runCodexStopHook() {
|
|
|
63
60
|
process.stdout.write(out);
|
|
64
61
|
}
|
|
65
62
|
var CODEX_PEEK_BUDGET_MS = CODEX_PEEK_MS;
|
|
66
|
-
|
|
67
63
|
export {
|
|
68
|
-
|
|
64
|
+
CODEX_PEEK_BUDGET_MS,
|
|
69
65
|
codexPendingMarkerPath,
|
|
66
|
+
decideCodexStopHook,
|
|
70
67
|
defaultPeekPending,
|
|
71
|
-
runCodexStopHook
|
|
72
|
-
CODEX_PEEK_BUDGET_MS
|
|
68
|
+
runCodexStopHook
|
|
73
69
|
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_BROKER_URL,
|
|
3
|
+
reconcileConnectPairSlot,
|
|
4
|
+
runWizard
|
|
5
|
+
} from "./chunk-GVWYW7MY.js";
|
|
6
|
+
import "./chunk-E6WMFMM2.js";
|
|
7
|
+
import "./chunk-OBIJ6WQP.js";
|
|
8
|
+
import "./chunk-QJFMU4QC.js";
|
|
9
|
+
import "./chunk-D6JKFJ6A.js";
|
|
10
|
+
import {
|
|
11
|
+
CONNECT_RUNTIMES
|
|
12
|
+
} from "./chunk-EIH2LNF4.js";
|
|
13
|
+
import "./chunk-RDLF4NQC.js";
|
|
14
|
+
import "./chunk-SQL56SEB.js";
|
|
15
|
+
import "./chunk-77HWBSRH.js";
|
|
16
|
+
import "./chunk-V5VZPYMZ.js";
|
|
17
|
+
import {
|
|
18
|
+
runPair
|
|
19
|
+
} from "./chunk-EIAUW6KO.js";
|
|
20
|
+
import {
|
|
21
|
+
loadPairedConfig,
|
|
22
|
+
pairedConfigPath,
|
|
23
|
+
perRuntimeConfigPath
|
|
24
|
+
} from "./chunk-5SZHXYPK.js";
|
|
25
|
+
import "./chunk-I67C2HYA.js";
|
|
26
|
+
import "./chunk-MIEI4PLB.js";
|
|
27
|
+
import {
|
|
28
|
+
defaultPairedKeystorePath,
|
|
29
|
+
deriveKeystorePath
|
|
30
|
+
} from "./chunk-6G6YYST6.js";
|
|
31
|
+
import "./chunk-U5HHHRXA.js";
|
|
32
|
+
import "./chunk-QEJUNP3X.js";
|
|
33
|
+
|
|
34
|
+
// src/wizard/connect-handler.ts
|
|
35
|
+
import os from "os";
|
|
36
|
+
import path from "path";
|
|
37
|
+
function absConfigPath(runtime) {
|
|
38
|
+
const p = path.resolve(perRuntimeConfigPath(runtime));
|
|
39
|
+
if (process.platform === "win32" && !/^[a-zA-Z]:/.test(p)) {
|
|
40
|
+
return path.resolve(path.join(os.homedir(), ".kojee", "agents", runtime, "config.json"));
|
|
41
|
+
}
|
|
42
|
+
return p;
|
|
43
|
+
}
|
|
44
|
+
function identityLabel(cfg) {
|
|
45
|
+
if (cfg?.agent_id && cfg?.principal_id) return `${cfg.agent_id} (${cfg.principal_id})`;
|
|
46
|
+
if (cfg?.agent_id) return cfg.agent_id;
|
|
47
|
+
if (cfg?.principal_id) return cfg.principal_id;
|
|
48
|
+
return "this runtime";
|
|
49
|
+
}
|
|
50
|
+
async function runConnect(opts) {
|
|
51
|
+
if (opts.runtime === "hermes") return runConnectHermes(opts);
|
|
52
|
+
const messages = [];
|
|
53
|
+
const runtime = opts.runtime;
|
|
54
|
+
if (!CONNECT_RUNTIMES.includes(runtime)) {
|
|
55
|
+
return {
|
|
56
|
+
messages,
|
|
57
|
+
output: `\`connect\` supports: ${CONNECT_RUNTIMES.join(", ")}, hermes. Unknown --runtime "${runtime}". (claude-code/codex/openclaw write a per-runtime paired slot; hermes writes the global ~/.kojee/config.json.)`,
|
|
58
|
+
exitCode: 2
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const url = (opts.url ?? DEFAULT_BROKER_URL).replace(/\/+$/, "");
|
|
62
|
+
const configPath = absConfigPath(runtime);
|
|
63
|
+
const oldConfig = loadPairedConfig(configPath);
|
|
64
|
+
const reconcile = reconcileConnectPairSlot(runtime, opts.code);
|
|
65
|
+
if (!reconcile.ok) {
|
|
66
|
+
return { messages, output: reconcile.output, exitCode: reconcile.exitCode };
|
|
67
|
+
}
|
|
68
|
+
messages.push(...reconcile.messages);
|
|
69
|
+
const pair = opts.runPair ?? runPair;
|
|
70
|
+
try {
|
|
71
|
+
const r = await pair({
|
|
72
|
+
code: opts.code,
|
|
73
|
+
url,
|
|
74
|
+
keystorePath: deriveKeystorePath(opts.code),
|
|
75
|
+
configPath,
|
|
76
|
+
runtime,
|
|
77
|
+
allowOverwrite: true
|
|
78
|
+
});
|
|
79
|
+
messages.push(r.message);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
return { messages, output: `Connect failed while pairing: ${err.message}`, exitCode: 1 };
|
|
82
|
+
}
|
|
83
|
+
const wizard = opts.runWizard ?? runWizard;
|
|
84
|
+
const wiz = await wizard({ runtime, pairedConfigPath: configPath, interactive: false });
|
|
85
|
+
if (wiz.exitCode !== 0) {
|
|
86
|
+
return { messages, output: wiz.output, exitCode: wiz.exitCode };
|
|
87
|
+
}
|
|
88
|
+
const newConfig = loadPairedConfig(configPath);
|
|
89
|
+
const newLabel = identityLabel(newConfig);
|
|
90
|
+
const summary = oldConfig ? `\u21BB re-connected ${runtime} as ${newLabel} (was ${identityLabel(oldConfig)})` : `\u2713 connected ${runtime} as ${newLabel} \u2192 ${url}`;
|
|
91
|
+
const output = `${summary}
|
|
92
|
+
Restart ${runtime} to pick up the connection.`;
|
|
93
|
+
return { messages, output, exitCode: 0 };
|
|
94
|
+
}
|
|
95
|
+
async function runConnectHermes(opts) {
|
|
96
|
+
const messages = [];
|
|
97
|
+
const url = (opts.url ?? DEFAULT_BROKER_URL).replace(/\/+$/, "");
|
|
98
|
+
const configPath = pairedConfigPath();
|
|
99
|
+
const oldConfig = loadPairedConfig(configPath);
|
|
100
|
+
const guard = reconcileConnectPairSlot("hermes", opts.code);
|
|
101
|
+
if (!guard.ok) {
|
|
102
|
+
return { messages, output: guard.output, exitCode: guard.exitCode };
|
|
103
|
+
}
|
|
104
|
+
messages.push(...guard.messages);
|
|
105
|
+
const pair = opts.runPair ?? runPair;
|
|
106
|
+
try {
|
|
107
|
+
const r = await pair({
|
|
108
|
+
code: opts.code,
|
|
109
|
+
url,
|
|
110
|
+
keystorePath: defaultPairedKeystorePath(),
|
|
111
|
+
configPath,
|
|
112
|
+
runtime: "hermes",
|
|
113
|
+
allowOverwrite: true
|
|
114
|
+
// connect is idempotent
|
|
115
|
+
});
|
|
116
|
+
messages.push(r.message);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return { messages, output: `Connect failed while pairing: ${err.message}`, exitCode: 1 };
|
|
119
|
+
}
|
|
120
|
+
const wizard = opts.runWizard ?? runWizard;
|
|
121
|
+
const wiz = await wizard({ runtime: "hermes", interactive: false });
|
|
122
|
+
if (wiz.exitCode !== 0) {
|
|
123
|
+
return { messages, output: wiz.output, exitCode: wiz.exitCode };
|
|
124
|
+
}
|
|
125
|
+
const label = identityLabel(loadPairedConfig(configPath));
|
|
126
|
+
const summary = oldConfig ? `\u21BB re-connected hermes as ${label}` : `\u2713 connected hermes as ${label} \u2192 ${url}`;
|
|
127
|
+
return {
|
|
128
|
+
messages,
|
|
129
|
+
output: `${summary}
|
|
130
|
+
Start (or restart) the hermes daemon to pick up the connection.`,
|
|
131
|
+
exitCode: 0
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
runConnect,
|
|
136
|
+
runConnectHermes
|
|
137
|
+
};
|