kojee-mcp 0.5.11 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/ancestry-ONFBQEP5.js +8 -0
- package/dist/{chunk-LDZXU3DW.js → chunk-2OLXXOT3.js} +1 -0
- package/dist/{codex-stop-hook-SWA53ECG.js → chunk-35XBRG3V.js} +4 -3
- package/dist/chunk-3H3TL34J.js +401 -0
- package/dist/{chunk-MKDMAAMN.js → chunk-74XFVX6Z.js} +16 -4
- package/dist/chunk-CM3EKMDD.js +116 -0
- package/dist/{chunk-DS26OORG.js → chunk-CO73VGWM.js} +41 -23
- package/dist/{chunk-HIZ4NDWN.js → chunk-DXJ6QLSJ.js} +22 -2
- package/dist/{chunk-HSR3GXCL.js → chunk-IMOEZ4NJ.js} +83 -6
- package/dist/{chunk-2MIISF2W.js → chunk-NR4Y54OL.js} +14 -1
- package/dist/chunk-XLKGPGZT.js +0 -0
- package/dist/chunk-XXFVWP6H.js +44 -0
- package/dist/cli.js +17 -15
- package/dist/codex-stop-hook-VY7DOMAG.js +16 -0
- package/dist/{doctor-XK335W7B.js → doctor-FVTALRQD.js} +110 -15
- package/dist/{event-log-B27VVEMK.js → event-log-VZD7NKYX.js} +1 -1
- package/dist/event-stream-XX5EZ6HN.js +19 -0
- package/dist/{gateway-client-93P1E0CZ.d.ts → gateway-client-C6yx1mfM.d.ts} +6 -1
- package/dist/{hook-server-37E2LUKJ.js → hook-server-T2Z444OV.js} +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -7
- package/dist/lib.d.ts +224 -3
- package/dist/lib.js +16 -11
- package/dist/{parent-watchdog-RZLHYP7T.js → parent-watchdog-TLU355FB.js} +1 -1
- package/dist/registry-TGALQP6M.js +348 -0
- package/dist/{send-cli-CN5EX7PO.js → send-cli-RH7D4JDP.js} +18 -10
- package/dist/server-LBVEDIXP.js +14 -0
- package/dist/{stop-hook-GEJF47SN.js → stop-hook-CUVDKXP7.js} +7 -6
- package/dist/{tail-stream-JNR4WFW3.js → tail-stream-5DAVRQYK.js} +4 -3
- package/dist/{user-prompt-submit-hook-DGRRFHOB.js → user-prompt-submit-hook-PMBUPKUV.js} +5 -5
- package/dist/{webhook-sink-NWGCUDGY.js → webhook-sink-N6AUTFL3.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-D42PZX2I.js +0 -784
- package/dist/{chunk-BJMASMKX.js → chunk-VHKPWUX7.js} +0 -0
- package/dist/{doctor-codex-SMROUYGV.js → doctor-codex-PA3WO6LR.js} +1 -1
- package/dist/{wizard-PLGHYCT3.js → wizard-L4MYRLJI.js} +11 -11
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
secureFile
|
|
6
6
|
} from "./chunk-BLEGIR35.js";
|
|
7
7
|
|
|
8
|
-
// src/
|
|
8
|
+
// src/delivery/lib/event-log.ts
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import os from "os";
|
|
11
11
|
import path from "path";
|
|
@@ -21,13 +21,24 @@ function statusLogPath(eventLogPath) {
|
|
|
21
21
|
const m = base.match(/^kojee-events-(.+)\.log$/);
|
|
22
22
|
return path.join(dir, m ? `kojee-status-${m[1]}.log` : `${base}.status`);
|
|
23
23
|
}
|
|
24
|
+
function livePathOwnedBySibling(candidatePath) {
|
|
25
|
+
try {
|
|
26
|
+
const { livePaths } = listActiveSessionIds();
|
|
27
|
+
return livePaths.has(path.resolve(candidatePath));
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
24
32
|
function startEventLog(opts) {
|
|
25
33
|
const dir = opts.dir ?? DEFAULT_DIR;
|
|
26
34
|
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
27
35
|
const statusMaxBytes = opts.statusMaxBytes ?? DEFAULT_STATUS_MAX_BYTES;
|
|
28
|
-
const filePath = path.join(dir, `kojee-events-${opts.key}.log`);
|
|
29
|
-
const statusPath = statusLogPath(filePath);
|
|
30
36
|
fs.mkdirSync(dir, { recursive: true });
|
|
37
|
+
let filePath = path.join(dir, `kojee-events-${opts.key}.log`);
|
|
38
|
+
if (opts.key.startsWith("wd-") && livePathOwnedBySibling(filePath)) {
|
|
39
|
+
filePath = path.join(dir, `kojee-events-${opts.key}-${process.pid}.log`);
|
|
40
|
+
}
|
|
41
|
+
const statusPath = statusLogPath(filePath);
|
|
31
42
|
fs.writeFileSync(filePath, "", { mode: 384 });
|
|
32
43
|
secureFile(filePath);
|
|
33
44
|
fs.writeFileSync(statusPath, "", { mode: 384 });
|
|
@@ -146,6 +157,19 @@ function nudgeSentinelPath(eventLogPath) {
|
|
|
146
157
|
const m = base.match(/^kojee-events-(.+)\.log$/);
|
|
147
158
|
return path.join(dir, m ? `kojee-nudge-${m[1]}.touch` : `${base}.nudge`);
|
|
148
159
|
}
|
|
160
|
+
function reapEventLogSiblings(messagesLogPath) {
|
|
161
|
+
for (const p of [
|
|
162
|
+
messagesLogPath,
|
|
163
|
+
statusLogPath(messagesLogPath),
|
|
164
|
+
monitorHeartbeatPath(messagesLogPath),
|
|
165
|
+
nudgeSentinelPath(messagesLogPath)
|
|
166
|
+
]) {
|
|
167
|
+
try {
|
|
168
|
+
fs.unlinkSync(p);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
149
173
|
function isProcessAlive(pid) {
|
|
150
174
|
try {
|
|
151
175
|
process.kill(pid, 0);
|
|
@@ -165,6 +189,7 @@ function listActiveSessionIds() {
|
|
|
165
189
|
} catch {
|
|
166
190
|
return { active, livePaths };
|
|
167
191
|
}
|
|
192
|
+
const dead = [];
|
|
168
193
|
for (const name of entries) {
|
|
169
194
|
if (!name.endsWith(".json")) continue;
|
|
170
195
|
const rawId = name.slice(0, -".json".length);
|
|
@@ -172,28 +197,25 @@ function listActiveSessionIds() {
|
|
|
172
197
|
const filePath = path.join(dir, name);
|
|
173
198
|
try {
|
|
174
199
|
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
175
|
-
|
|
200
|
+
const livenessPid = data.proxyPid ?? data.pid;
|
|
201
|
+
if (typeof livenessPid === "number" && isProcessAlive(livenessPid)) {
|
|
176
202
|
active.add(sessionId);
|
|
177
203
|
if (data.eventLogPath) livePaths.add(path.resolve(data.eventLogPath));
|
|
178
204
|
} else {
|
|
179
|
-
|
|
180
|
-
fs.unlinkSync(filePath);
|
|
181
|
-
} catch {
|
|
182
|
-
}
|
|
183
|
-
if (data.eventLogPath) {
|
|
184
|
-
try {
|
|
185
|
-
fs.unlinkSync(data.eventLogPath);
|
|
186
|
-
} catch {
|
|
187
|
-
}
|
|
188
|
-
try {
|
|
189
|
-
fs.unlinkSync(statusLogPath(data.eventLogPath));
|
|
190
|
-
} catch {
|
|
191
|
-
}
|
|
192
|
-
}
|
|
205
|
+
dead.push({ filePath, eventLogPath: data.eventLogPath });
|
|
193
206
|
}
|
|
194
207
|
} catch {
|
|
195
208
|
}
|
|
196
209
|
}
|
|
210
|
+
for (const { filePath, eventLogPath } of dead) {
|
|
211
|
+
try {
|
|
212
|
+
fs.unlinkSync(filePath);
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
215
|
+
if (eventLogPath && !livePaths.has(path.resolve(eventLogPath))) {
|
|
216
|
+
reapEventLogSiblings(eventLogPath);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
197
219
|
return { active, livePaths };
|
|
198
220
|
}
|
|
199
221
|
function sweepStaleEventLogs(dir = DEFAULT_DIR, minAgeMs = DEFAULT_MIN_AGE_MS) {
|
|
@@ -219,11 +241,7 @@ function sweepStaleEventLogs(dir = DEFAULT_DIR, minAgeMs = DEFAULT_MIN_AGE_MS) {
|
|
|
219
241
|
const stat = fs.statSync(filePath);
|
|
220
242
|
const ageMs = Math.max(0, now - stat.mtimeMs);
|
|
221
243
|
if (ageMs < minAgeMs) continue;
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
fs.unlinkSync(statusLogPath(filePath));
|
|
225
|
-
} catch {
|
|
226
|
-
}
|
|
244
|
+
reapEventLogSiblings(filePath);
|
|
227
245
|
} catch {
|
|
228
246
|
}
|
|
229
247
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
translateToolCallResult
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2OLXXOT3.js";
|
|
4
4
|
|
|
5
5
|
// src/tandem/send.ts
|
|
6
6
|
var SEND_KINDS = ["message", "status"];
|
|
@@ -52,7 +52,7 @@ function classifySendFailure(text) {
|
|
|
52
52
|
if (/token is invalid or expired/i.test(text) || /authentication failed/i.test(text) || /re-?authoriz/i.test(text)) {
|
|
53
53
|
return sendFailure("gateway_auth", text);
|
|
54
54
|
}
|
|
55
|
-
if (/aren'?t a member|not a member/i.test(text)) {
|
|
55
|
+
if (/aren'?t a member|not[ _]a[ _]member|aren'?t an active member/i.test(text)) {
|
|
56
56
|
return sendFailure("not_member", text);
|
|
57
57
|
}
|
|
58
58
|
if (/rate limit/i.test(text)) {
|
|
@@ -110,6 +110,25 @@ async function executeSend(gateway, request) {
|
|
|
110
110
|
text
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
|
+
async function executeJoin(gateway, tandemId) {
|
|
114
|
+
let result;
|
|
115
|
+
try {
|
|
116
|
+
result = await gateway.sendRpc("tools/call", {
|
|
117
|
+
name: "tandem_join",
|
|
118
|
+
arguments: { tandem_id: tandemId }
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
return classifySendFailure(
|
|
122
|
+
`tandem_join failed: ${err?.message ?? String(err)}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const translated = translateToolCallResult(result);
|
|
126
|
+
const text = (translated.content ?? []).map((c) => typeof c?.text === "string" ? c.text : "").filter(Boolean).join("\n");
|
|
127
|
+
if (translated.isError) {
|
|
128
|
+
return classifySendFailure(text || "tandem_join returned an error with no text");
|
|
129
|
+
}
|
|
130
|
+
return { ok: true, tandem_id: tandemId, message_id: null, cursor: null, text };
|
|
131
|
+
}
|
|
113
132
|
var HTTP_STATUS_BY_CODE = {
|
|
114
133
|
bad_request: 400,
|
|
115
134
|
unauthorized: 401,
|
|
@@ -137,5 +156,6 @@ export {
|
|
|
137
156
|
sendFailure,
|
|
138
157
|
parseSendRequest,
|
|
139
158
|
executeSend,
|
|
159
|
+
executeJoin,
|
|
140
160
|
httpStatusForEnvelope
|
|
141
161
|
};
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
createDPoPProof,
|
|
3
|
+
deriveStableSessionId,
|
|
4
|
+
getSessionId,
|
|
5
|
+
setStableSessionId
|
|
6
|
+
} from "./chunk-NR4Y54OL.js";
|
|
5
7
|
import {
|
|
6
8
|
translateHttpError,
|
|
7
9
|
translateJsonRpcError,
|
|
8
10
|
translateNetworkError
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-2OLXXOT3.js";
|
|
12
|
+
import {
|
|
13
|
+
findClaudeAncestorPid
|
|
14
|
+
} from "./chunk-VHKPWUX7.js";
|
|
10
15
|
|
|
11
16
|
// src/gateway-client.ts
|
|
12
17
|
import crypto from "crypto";
|
|
@@ -129,7 +134,7 @@ var GatewayClient = class {
|
|
|
129
134
|
"Content-Type": "application/json",
|
|
130
135
|
Authorization: `DPoP ${this.token}`,
|
|
131
136
|
DPoP: proof,
|
|
132
|
-
"Mcp-Session-Id":
|
|
137
|
+
"Mcp-Session-Id": getSessionId()
|
|
133
138
|
},
|
|
134
139
|
body: JSON.stringify(rpcRequest),
|
|
135
140
|
// ROUND-3 MAJOR A: the caller's AbortSignal rides HERE (a real fetch
|
|
@@ -153,6 +158,78 @@ var GatewayClient = class {
|
|
|
153
158
|
}
|
|
154
159
|
};
|
|
155
160
|
|
|
161
|
+
// src/runtime/cc-session-id.ts
|
|
162
|
+
import fs from "fs";
|
|
163
|
+
import { execFileSync } from "child_process";
|
|
164
|
+
import { createHash } from "crypto";
|
|
165
|
+
var CC_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
166
|
+
function extractCcSessionId(raw) {
|
|
167
|
+
if (raw.includes("\0")) {
|
|
168
|
+
for (const entry of raw.split("\0")) {
|
|
169
|
+
if (entry.startsWith(`${CC_SESSION_ENV}=`)) {
|
|
170
|
+
const v = entry.slice(CC_SESSION_ENV.length + 1);
|
|
171
|
+
return v.length > 0 ? v : null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
const re = new RegExp(`(?:^|\\s)${CC_SESSION_ENV}=([^\\s]+)`, "g");
|
|
177
|
+
let last = null;
|
|
178
|
+
let m;
|
|
179
|
+
while ((m = re.exec(raw)) !== null) last = m[1];
|
|
180
|
+
return last;
|
|
181
|
+
}
|
|
182
|
+
function defaultReadProcessEnvRaw(pid, platform) {
|
|
183
|
+
try {
|
|
184
|
+
if (platform === "linux") {
|
|
185
|
+
return fs.readFileSync(`/proc/${pid}/environ`, "utf8");
|
|
186
|
+
}
|
|
187
|
+
if (platform === "darwin") {
|
|
188
|
+
return execFileSync("ps", ["eww", "-p", String(pid), "-o", "command="], {
|
|
189
|
+
encoding: "utf8",
|
|
190
|
+
timeout: 2e3
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
function sanitizeKey(value) {
|
|
199
|
+
return value.replace(/[^A-Za-z0-9_-]/g, "");
|
|
200
|
+
}
|
|
201
|
+
function resolveInstanceKey(deps = {}) {
|
|
202
|
+
const env = deps.env ?? process.env;
|
|
203
|
+
const platform = deps.platform ?? process.platform;
|
|
204
|
+
const ccPid = deps.ccPid ?? null;
|
|
205
|
+
if (ccPid !== null) {
|
|
206
|
+
const reader = deps.readProcessEnvRaw ?? defaultReadProcessEnvRaw;
|
|
207
|
+
const raw = reader(ccPid, platform);
|
|
208
|
+
if (raw) {
|
|
209
|
+
const sid = sanitizeKey(extractCcSessionId(raw) ?? "");
|
|
210
|
+
if (sid) return sid;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const explicit = sanitizeKey((env.KOJEE_INSTANCE ?? "").trim());
|
|
214
|
+
if (explicit) return `inst-${explicit}`;
|
|
215
|
+
const base = deps.projectDir ?? env.CLAUDE_PROJECT_DIR ?? deps.cwd ?? process.cwd() ?? "";
|
|
216
|
+
const hash = createHash("sha256").update(base).digest("hex").slice(0, 12);
|
|
217
|
+
return `wd-${hash}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/runtime/stable-session.ts
|
|
221
|
+
async function applyStableSessionId(token, deps = {}) {
|
|
222
|
+
let instanceKey = deps.instanceKey;
|
|
223
|
+
if (instanceKey === void 0) {
|
|
224
|
+
const ccPid = deps.ccPid !== void 0 ? deps.ccPid : await findClaudeAncestorPid();
|
|
225
|
+
instanceKey = resolveInstanceKey({ ccPid });
|
|
226
|
+
}
|
|
227
|
+
const sessionId = deriveStableSessionId(token, instanceKey);
|
|
228
|
+
setStableSessionId(sessionId);
|
|
229
|
+
return { instanceKey, sessionId };
|
|
230
|
+
}
|
|
231
|
+
|
|
156
232
|
export {
|
|
157
|
-
GatewayClient
|
|
233
|
+
GatewayClient,
|
|
234
|
+
applyStableSessionId
|
|
158
235
|
};
|
|
@@ -26,10 +26,23 @@ function computeAth(accessToken) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// src/tandem/session-id.ts
|
|
29
|
+
import crypto2 from "crypto";
|
|
29
30
|
import { ulid } from "ulidx";
|
|
30
31
|
var MCP_SESSION_ID = ulid();
|
|
32
|
+
function deriveStableSessionId(token, instanceKey) {
|
|
33
|
+
return crypto2.createHash("sha256").update(`${token}:${instanceKey}`).digest("hex").slice(0, 32);
|
|
34
|
+
}
|
|
35
|
+
var _stableSessionId = null;
|
|
36
|
+
function setStableSessionId(id) {
|
|
37
|
+
_stableSessionId = id;
|
|
38
|
+
}
|
|
39
|
+
function getSessionId() {
|
|
40
|
+
return _stableSessionId ?? MCP_SESSION_ID;
|
|
41
|
+
}
|
|
31
42
|
|
|
32
43
|
export {
|
|
33
44
|
createDPoPProof,
|
|
34
|
-
|
|
45
|
+
deriveStableSessionId,
|
|
46
|
+
setStableSessionId,
|
|
47
|
+
getSessionId
|
|
35
48
|
};
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildReplyRecipe
|
|
3
|
+
} from "./chunk-X672ZN7V.js";
|
|
4
|
+
|
|
5
|
+
// src/adapters/claude-code.ts
|
|
6
|
+
function computeSeverity(event) {
|
|
7
|
+
if (event.type === "state_change") return "high";
|
|
8
|
+
if (event.mentions && event.mentions.length > 0) {
|
|
9
|
+
return "high";
|
|
10
|
+
}
|
|
11
|
+
return "normal";
|
|
12
|
+
}
|
|
13
|
+
function formatBody(event) {
|
|
14
|
+
if (event.type === "state_change") {
|
|
15
|
+
return `[Tandem: ${event.tandem_id}] ${event.from.displayname}: ${event.content.body}`;
|
|
16
|
+
}
|
|
17
|
+
return [
|
|
18
|
+
`[Tandem: ${event.tandem_id}] ${event.from.displayname} (${event.from.principal}) \u2014 ${event.kind}:`,
|
|
19
|
+
event.content.body,
|
|
20
|
+
"",
|
|
21
|
+
`> ${buildReplyRecipe({ tandem_id: event.tandem_id, message_id: event.id })}`
|
|
22
|
+
].join("\n");
|
|
23
|
+
}
|
|
24
|
+
var claudeCodeAdapter = {
|
|
25
|
+
runtime: "claude-code",
|
|
26
|
+
supportsChannels: true,
|
|
27
|
+
formatTandemEvent(event) {
|
|
28
|
+
const meta = {
|
|
29
|
+
tandem_id: event.tandem_id,
|
|
30
|
+
message_id: event.id,
|
|
31
|
+
cursor: String(event.cursor),
|
|
32
|
+
kind: event.kind,
|
|
33
|
+
from_principal: event.from.principal,
|
|
34
|
+
from_display: event.from.displayname,
|
|
35
|
+
severity: computeSeverity(event)
|
|
36
|
+
};
|
|
37
|
+
if (event.wake_reason) meta.wake_reason = event.wake_reason;
|
|
38
|
+
return { content: formatBody(event), meta };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
claudeCodeAdapter
|
|
44
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -3,25 +3,27 @@ import {
|
|
|
3
3
|
runPair
|
|
4
4
|
} from "./chunk-OGHDTFAX.js";
|
|
5
5
|
import {
|
|
6
|
-
VERSION,
|
|
7
6
|
startProxy
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-BJMASMKX.js";
|
|
7
|
+
} from "./chunk-3H3TL34J.js";
|
|
8
|
+
import "./chunk-XXFVWP6H.js";
|
|
11
9
|
import {
|
|
12
10
|
pairedConfigPath
|
|
13
11
|
} from "./chunk-YH27B6SW.js";
|
|
12
|
+
import "./chunk-IMOEZ4NJ.js";
|
|
14
13
|
import "./chunk-JXMVZEQ7.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-MKDMAAMN.js";
|
|
17
|
-
import "./chunk-2MIISF2W.js";
|
|
14
|
+
import "./chunk-NR4Y54OL.js";
|
|
18
15
|
import {
|
|
19
16
|
defaultPairedKeystorePath,
|
|
20
17
|
deriveKeystorePath
|
|
21
18
|
} from "./chunk-CH32ELFX.js";
|
|
22
19
|
import "./chunk-BLEGIR35.js";
|
|
20
|
+
import {
|
|
21
|
+
VERSION
|
|
22
|
+
} from "./chunk-CM3EKMDD.js";
|
|
23
|
+
import "./chunk-X672ZN7V.js";
|
|
24
|
+
import "./chunk-2OLXXOT3.js";
|
|
25
|
+
import "./chunk-VHKPWUX7.js";
|
|
23
26
|
import "./chunk-YKW54DKF.js";
|
|
24
|
-
import "./chunk-LDZXU3DW.js";
|
|
25
27
|
|
|
26
28
|
// src/cli.ts
|
|
27
29
|
import { Command } from "commander";
|
|
@@ -43,15 +45,15 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
43
45
|
});
|
|
44
46
|
program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption("--type <type>", "Hook type: stop, user-prompt-submit, or codex-stop").action(async (opts) => {
|
|
45
47
|
if (opts.type === "stop") {
|
|
46
|
-
const { runStopHook } = await import("./stop-hook-
|
|
48
|
+
const { runStopHook } = await import("./stop-hook-CUVDKXP7.js");
|
|
47
49
|
await runStopHook();
|
|
48
50
|
process.exit(0);
|
|
49
51
|
} else if (opts.type === "user-prompt-submit") {
|
|
50
|
-
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-
|
|
52
|
+
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-PMBUPKUV.js");
|
|
51
53
|
await runUserPromptSubmitHook();
|
|
52
54
|
process.exit(0);
|
|
53
55
|
} else if (opts.type === "codex-stop") {
|
|
54
|
-
const { runCodexStopHook } = await import("./codex-stop-hook-
|
|
56
|
+
const { runCodexStopHook } = await import("./codex-stop-hook-VY7DOMAG.js");
|
|
55
57
|
await runCodexStopHook();
|
|
56
58
|
process.exit(0);
|
|
57
59
|
} else {
|
|
@@ -77,7 +79,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
77
79
|
program.command("send <tandem_id>").description(
|
|
78
80
|
"Send a Tandem message using this machine's paired credentials (~/.kojee). Prints one JSON envelope to stdout: {ok, message_id, cursor, text} on success, {ok:false, error:<typed code>, message} on failure (exit 1)."
|
|
79
81
|
).requiredOption("--body <text>", "Message body (required)").option("--reply-to <message_id>", "Message id this send replies to").option("--kind <kind>", "Message kind: message | status (default: backend default)").action(async (tandemId, opts) => {
|
|
80
|
-
const { runSendCli } = await import("./send-cli-
|
|
82
|
+
const { runSendCli } = await import("./send-cli-RH7D4JDP.js");
|
|
81
83
|
const { exitCode, envelope } = await runSendCli({
|
|
82
84
|
tandemId,
|
|
83
85
|
body: opts.body,
|
|
@@ -88,7 +90,7 @@ program.command("send <tandem_id>").description(
|
|
|
88
90
|
process.exit(exitCode);
|
|
89
91
|
});
|
|
90
92
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
91
|
-
const { runTail } = await import("./tail-stream-
|
|
93
|
+
const { runTail } = await import("./tail-stream-5DAVRQYK.js");
|
|
92
94
|
try {
|
|
93
95
|
await runTail(filePath);
|
|
94
96
|
} catch (err) {
|
|
@@ -97,7 +99,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
97
99
|
}
|
|
98
100
|
});
|
|
99
101
|
program.command("doctor").description("Diagnose the kojee wake path (proxy, hook-server, SSE stream, event log, Monitor) and print the exact wake recipe").action(async () => {
|
|
100
|
-
const { runDoctor } = await import("./doctor-
|
|
102
|
+
const { runDoctor } = await import("./doctor-FVTALRQD.js");
|
|
101
103
|
const code = await runDoctor();
|
|
102
104
|
process.exit(code);
|
|
103
105
|
});
|
|
@@ -138,7 +140,7 @@ program.command("init").description(
|
|
|
138
140
|
console.error("Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run `init` \u2014 or pass --token/--pair-code, or run `init` in a terminal for the guided wizard.");
|
|
139
141
|
process.exit(1);
|
|
140
142
|
}
|
|
141
|
-
const { runWizard } = await import("./wizard-
|
|
143
|
+
const { runWizard } = await import("./wizard-L4MYRLJI.js");
|
|
142
144
|
const result = await runWizard({
|
|
143
145
|
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
|
144
146
|
...opts.uninstall ? { uninstall: true } : {},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODEX_PEEK_BUDGET_MS,
|
|
3
|
+
codexPendingMarkerPath,
|
|
4
|
+
decideCodexStopHook,
|
|
5
|
+
defaultPeekPending,
|
|
6
|
+
runCodexStopHook
|
|
7
|
+
} from "./chunk-35XBRG3V.js";
|
|
8
|
+
import "./chunk-LSUB6QMP.js";
|
|
9
|
+
import "./chunk-X672ZN7V.js";
|
|
10
|
+
export {
|
|
11
|
+
CODEX_PEEK_BUDGET_MS,
|
|
12
|
+
codexPendingMarkerPath,
|
|
13
|
+
decideCodexStopHook,
|
|
14
|
+
defaultPeekPending,
|
|
15
|
+
runCodexStopHook
|
|
16
|
+
};
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import "./chunk-XLKGPGZT.js";
|
|
2
|
+
import {
|
|
3
|
+
loadControlToken
|
|
4
|
+
} from "./chunk-GI2CKKBL.js";
|
|
1
5
|
import {
|
|
2
6
|
monitorHeartbeatPath,
|
|
3
7
|
statusLogPath
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CO73VGWM.js";
|
|
5
9
|
import {
|
|
6
10
|
discoveryPathForKey,
|
|
7
|
-
readSessionDiscoveryByKey
|
|
11
|
+
readSessionDiscoveryByKey,
|
|
12
|
+
sessionDiscoveryDir
|
|
8
13
|
} from "./chunk-DO42NPNR.js";
|
|
9
14
|
import {
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
15
|
+
loadPairedConfig
|
|
16
|
+
} from "./chunk-YH27B6SW.js";
|
|
17
|
+
import "./chunk-BLEGIR35.js";
|
|
12
18
|
import {
|
|
13
19
|
buildMonitorSpawn,
|
|
14
20
|
buildReplyRecipe
|
|
@@ -16,14 +22,74 @@ import {
|
|
|
16
22
|
import {
|
|
17
23
|
deriveDiscoveryKey,
|
|
18
24
|
findClaudeAncestorPid
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import {
|
|
21
|
-
loadPairedConfig
|
|
22
|
-
} from "./chunk-YH27B6SW.js";
|
|
23
|
-
import "./chunk-BLEGIR35.js";
|
|
25
|
+
} from "./chunk-VHKPWUX7.js";
|
|
24
26
|
|
|
25
27
|
// src/doctor.ts
|
|
26
28
|
import fs from "fs";
|
|
29
|
+
import os from "os";
|
|
30
|
+
import path from "path";
|
|
31
|
+
var SENTINEL_FRESH_MS = 5e3;
|
|
32
|
+
function eventLogKey(logPath) {
|
|
33
|
+
const m = logPath.replace(/^.*[/\\]/, "").match(/^kojee-events-(.+)\.log$/);
|
|
34
|
+
return m ? m[1] : null;
|
|
35
|
+
}
|
|
36
|
+
function defaultListSiblingProxies(selfKey) {
|
|
37
|
+
const dir = sessionDiscoveryDir();
|
|
38
|
+
let names;
|
|
39
|
+
try {
|
|
40
|
+
names = fs.readdirSync(dir);
|
|
41
|
+
} catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const name of names) {
|
|
46
|
+
if (!name.startsWith("cc-") || !name.endsWith(".json")) continue;
|
|
47
|
+
const key = name.slice("cc-".length, -".json".length);
|
|
48
|
+
if (key === selfKey) continue;
|
|
49
|
+
try {
|
|
50
|
+
const entry = JSON.parse(fs.readFileSync(path.join(dir, name), "utf8"));
|
|
51
|
+
const proxyPid = entry.proxyPid ?? entry.pid;
|
|
52
|
+
const proxyPidAlive = typeof proxyPid === "number" && defaultIsPidAlive(proxyPid);
|
|
53
|
+
let webhookStatusLine = null;
|
|
54
|
+
if (entry.eventLogPath) {
|
|
55
|
+
const last = defaultReadLastStatusLine(statusLogPath(entry.eventLogPath));
|
|
56
|
+
if (last && last.includes("status=webhook enabled")) webhookStatusLine = last;
|
|
57
|
+
}
|
|
58
|
+
out.push({ key, proxyPidAlive, webhookStatusLine });
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
async function resolveThisWebhookEnabled() {
|
|
65
|
+
try {
|
|
66
|
+
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
67
|
+
return resolveWebhookConfig().enabled === true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function defaultListMonitorSentinels() {
|
|
73
|
+
const dir = os.tmpdir();
|
|
74
|
+
let names;
|
|
75
|
+
try {
|
|
76
|
+
names = fs.readdirSync(dir);
|
|
77
|
+
} catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const name of names) {
|
|
82
|
+
const m = name.match(/^kojee-monitor-(.+)\.alive$/);
|
|
83
|
+
if (!m) continue;
|
|
84
|
+
const p = path.join(dir, name);
|
|
85
|
+
try {
|
|
86
|
+
const s = fs.statSync(p);
|
|
87
|
+
out.push({ path: p, key: m[1], mtimeMs: s.mtimeMs });
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
27
93
|
function defaultIsPidAlive(pid) {
|
|
28
94
|
try {
|
|
29
95
|
process.kill(pid, 0);
|
|
@@ -33,23 +99,23 @@ function defaultIsPidAlive(pid) {
|
|
|
33
99
|
return false;
|
|
34
100
|
}
|
|
35
101
|
}
|
|
36
|
-
function defaultStat(
|
|
102
|
+
function defaultStat(path2) {
|
|
37
103
|
try {
|
|
38
|
-
const s = fs.statSync(
|
|
104
|
+
const s = fs.statSync(path2);
|
|
39
105
|
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
40
106
|
} catch {
|
|
41
107
|
return null;
|
|
42
108
|
}
|
|
43
109
|
}
|
|
44
110
|
var STATUS_TAIL_BYTES = 64 * 1024;
|
|
45
|
-
function defaultReadLastStatusLine(
|
|
111
|
+
function defaultReadLastStatusLine(path2) {
|
|
46
112
|
let fd = null;
|
|
47
113
|
try {
|
|
48
|
-
const { size } = fs.statSync(
|
|
114
|
+
const { size } = fs.statSync(path2);
|
|
49
115
|
const start = Math.max(0, size - STATUS_TAIL_BYTES);
|
|
50
116
|
const len = size - start;
|
|
51
117
|
if (len === 0) return null;
|
|
52
|
-
fd = fs.openSync(
|
|
118
|
+
fd = fs.openSync(path2, "r");
|
|
53
119
|
const buf = Buffer.alloc(len);
|
|
54
120
|
fs.readSync(fd, buf, 0, len, start);
|
|
55
121
|
const text = buf.toString("utf8");
|
|
@@ -181,6 +247,35 @@ async function collectDoctorReport(deps = {}) {
|
|
|
181
247
|
detail: sentinelFresh ? "live (a Monitor is reading the log)" : "no fresh sentinel \u2014 spawn the Monitor (recipe below)"
|
|
182
248
|
});
|
|
183
249
|
}
|
|
250
|
+
const webhookSinkEnabled = deps.webhookSinkEnabled !== void 0 ? deps.webhookSinkEnabled : await resolveThisWebhookEnabled();
|
|
251
|
+
if (!webhookSinkEnabled) {
|
|
252
|
+
const listSiblings = deps.listSiblingProxies ?? defaultListSiblingProxies;
|
|
253
|
+
const siblingsWithWebhook = listSiblings(discoveryKey).filter(
|
|
254
|
+
(s) => s.proxyPidAlive && s.webhookStatusLine
|
|
255
|
+
);
|
|
256
|
+
if (siblingsWithWebhook.length > 0) {
|
|
257
|
+
checks.push({
|
|
258
|
+
name: "sibling webhook sink",
|
|
259
|
+
ok: "warn",
|
|
260
|
+
detail: `another live proxy has a webhook sink enabled while this session's is OFF \u2014 Tandem events may be delivered via that sibling's webhook, not your Monitor. Sibling key(s): ${siblingsWithWebhook.map((s) => s.key).join(", ")}.`
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (logPath) {
|
|
265
|
+
const currentKey = eventLogKey(logPath);
|
|
266
|
+
const listSentinels = deps.listMonitorSentinels ?? defaultListMonitorSentinels;
|
|
267
|
+
const now = Date.now();
|
|
268
|
+
const staleLiveMonitors = listSentinels().filter(
|
|
269
|
+
(s) => now - s.mtimeMs < SENTINEL_FRESH_MS && s.key !== currentKey
|
|
270
|
+
);
|
|
271
|
+
if (staleLiveMonitors.length > 0) {
|
|
272
|
+
checks.push({
|
|
273
|
+
name: "stale Monitor",
|
|
274
|
+
ok: "warn",
|
|
275
|
+
detail: `a live Monitor is tailing ${staleLiveMonitors.map((s) => s.path).join(", ")}, but current events go to ${logPath} \u2014 re-spawn the Monitor with the recipe below.`
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
184
279
|
checks.push({
|
|
185
280
|
name: "wire contract",
|
|
186
281
|
ok: "unknown",
|
|
@@ -233,7 +328,7 @@ function formatDoctorReport(report) {
|
|
|
233
328
|
async function runDoctor() {
|
|
234
329
|
const { readRecordedRuntime } = await import("./runtime-record-WO4IECM6.js");
|
|
235
330
|
if (readRecordedRuntime() === "codex") {
|
|
236
|
-
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-
|
|
331
|
+
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-PA3WO6LR.js");
|
|
237
332
|
const report2 = collectCodexDoctorReport();
|
|
238
333
|
console.error(formatCodexDoctorReport(report2));
|
|
239
334
|
return report2.verdict === "broken" ? 1 : 0;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
3
|
+
createAdaptiveWatchdog,
|
|
4
|
+
createBackoffController,
|
|
5
|
+
normalizeBackendEvent,
|
|
6
|
+
sanitizeDisplayname,
|
|
7
|
+
serializeCursorMap,
|
|
8
|
+
startEventStream
|
|
9
|
+
} from "./chunk-74XFVX6Z.js";
|
|
10
|
+
import "./chunk-NR4Y54OL.js";
|
|
11
|
+
export {
|
|
12
|
+
UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
13
|
+
createAdaptiveWatchdog,
|
|
14
|
+
createBackoffController,
|
|
15
|
+
normalizeBackendEvent,
|
|
16
|
+
sanitizeDisplayname,
|
|
17
|
+
serializeCursorMap,
|
|
18
|
+
startEventStream
|
|
19
|
+
};
|
|
@@ -26,6 +26,11 @@ interface LoadedKeyPair {
|
|
|
26
26
|
publicJwk: JWK;
|
|
27
27
|
kid: string;
|
|
28
28
|
}
|
|
29
|
+
interface McpToolDefinition {
|
|
30
|
+
name: string;
|
|
31
|
+
description: string;
|
|
32
|
+
inputSchema: Record<string, unknown>;
|
|
33
|
+
}
|
|
29
34
|
interface GovernanceMeta {
|
|
30
35
|
decision: "require_approval" | "deny" | "allow";
|
|
31
36
|
approval_id?: string;
|
|
@@ -89,4 +94,4 @@ declare class GatewayClient {
|
|
|
89
94
|
private tryParseErrorBody;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
export { GatewayClient as G, type KeystoreData as K, type LoadedKeyPair as L, type ProxyConfig as P, type ToolCallResult as T };
|
|
97
|
+
export { GatewayClient as G, type KeystoreData as K, type LoadedKeyPair as L, type McpToolDefinition as M, type ProxyConfig as P, type ToolCallResult as T };
|