kojee-mcp 0.5.12 → 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/dist/{chunk-LDZXU3DW.js → chunk-2OLXXOT3.js} +1 -0
- package/dist/{chunk-FQZCENSG.js → chunk-3H3TL34J.js} +16 -74
- package/dist/{chunk-5XP2UOFK.js → chunk-74XFVX6Z.js} +4 -4
- package/dist/{chunk-WLMPCX7T.js → chunk-CM3EKMDD.js} +1 -1
- 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/cli.js +7 -7
- package/dist/{event-stream-FOT7MJZH.js → event-stream-XX5EZ6HN.js} +2 -2
- package/dist/{hook-server-37E2LUKJ.js → hook-server-T2Z444OV.js} +2 -2
- package/dist/index.js +5 -5
- package/dist/lib.d.ts +44 -1
- package/dist/lib.js +11 -6
- package/dist/{registry-A3VT6VJD.js → registry-TGALQP6M.js} +7 -7
- package/dist/{send-cli-NZP5XE7T.js → send-cli-RH7D4JDP.js} +14 -6
- package/dist/{server-77QRWKJM.js → server-LBVEDIXP.js} +2 -2
- package/dist/{tail-stream-VZ462ZON.js → tail-stream-5DAVRQYK.js} +2 -2
- package/package.json +1 -1
|
@@ -62,6 +62,7 @@ function translateHttpError(status, errorCode, trigger) {
|
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
64
|
var TANDEM_ERROR_MESSAGES = {
|
|
65
|
+
[-32001]: () => "You aren't an active member of that Tandem (the seat may have been reaped). Run tandem_join to reactivate it, then retry.",
|
|
65
66
|
[-32003]: () => "This Tandem is hardened to owner-only membership; you can't join.",
|
|
66
67
|
[-32004]: () => "You aren't a member of that Tandem. Use tandem_join(join_link) first.",
|
|
67
68
|
[-32006]: (data) => {
|
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
claudeCodeAdapter
|
|
3
3
|
} from "./chunk-XXFVWP6H.js";
|
|
4
4
|
import {
|
|
5
|
-
GatewayClient
|
|
6
|
-
|
|
5
|
+
GatewayClient,
|
|
6
|
+
applyStableSessionId
|
|
7
|
+
} from "./chunk-IMOEZ4NJ.js";
|
|
7
8
|
import {
|
|
8
9
|
AuthModule
|
|
9
10
|
} from "./chunk-JXMVZEQ7.js";
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
import {
|
|
15
16
|
createMcpServer,
|
|
16
17
|
startMcpServer
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-CM3EKMDD.js";
|
|
18
19
|
import {
|
|
19
20
|
findClaudeAncestorPid
|
|
20
21
|
} from "./chunk-VHKPWUX7.js";
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
} from "./chunk-YKW54DKF.js";
|
|
24
25
|
|
|
25
26
|
// src/index.ts
|
|
26
|
-
import
|
|
27
|
+
import fs2 from "fs";
|
|
27
28
|
import os2 from "os";
|
|
28
29
|
import path2 from "path";
|
|
29
30
|
|
|
@@ -149,67 +150,8 @@ var unknownAdapter = {
|
|
|
149
150
|
}
|
|
150
151
|
};
|
|
151
152
|
|
|
152
|
-
// src/runtime/cc-session-id.ts
|
|
153
|
-
import fs from "fs";
|
|
154
|
-
import { execFileSync } from "child_process";
|
|
155
|
-
import { createHash } from "crypto";
|
|
156
|
-
var CC_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
157
|
-
function extractCcSessionId(raw) {
|
|
158
|
-
if (raw.includes("\0")) {
|
|
159
|
-
for (const entry of raw.split("\0")) {
|
|
160
|
-
if (entry.startsWith(`${CC_SESSION_ENV}=`)) {
|
|
161
|
-
const v = entry.slice(CC_SESSION_ENV.length + 1);
|
|
162
|
-
return v.length > 0 ? v : null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
const re = new RegExp(`(?:^|\\s)${CC_SESSION_ENV}=([^\\s]+)`, "g");
|
|
168
|
-
let last = null;
|
|
169
|
-
let m;
|
|
170
|
-
while ((m = re.exec(raw)) !== null) last = m[1];
|
|
171
|
-
return last;
|
|
172
|
-
}
|
|
173
|
-
function defaultReadProcessEnvRaw(pid, platform) {
|
|
174
|
-
try {
|
|
175
|
-
if (platform === "linux") {
|
|
176
|
-
return fs.readFileSync(`/proc/${pid}/environ`, "utf8");
|
|
177
|
-
}
|
|
178
|
-
if (platform === "darwin") {
|
|
179
|
-
return execFileSync("ps", ["eww", "-p", String(pid), "-o", "command="], {
|
|
180
|
-
encoding: "utf8",
|
|
181
|
-
timeout: 2e3
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
} catch {
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
function sanitizeKey(value) {
|
|
190
|
-
return value.replace(/[^A-Za-z0-9_-]/g, "");
|
|
191
|
-
}
|
|
192
|
-
function resolveInstanceKey(deps = {}) {
|
|
193
|
-
const env = deps.env ?? process.env;
|
|
194
|
-
const platform = deps.platform ?? process.platform;
|
|
195
|
-
const ccPid = deps.ccPid ?? null;
|
|
196
|
-
if (ccPid !== null) {
|
|
197
|
-
const reader = deps.readProcessEnvRaw ?? defaultReadProcessEnvRaw;
|
|
198
|
-
const raw = reader(ccPid, platform);
|
|
199
|
-
if (raw) {
|
|
200
|
-
const sid = sanitizeKey(extractCcSessionId(raw) ?? "");
|
|
201
|
-
if (sid) return sid;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const explicit = sanitizeKey((env.KOJEE_INSTANCE ?? "").trim());
|
|
205
|
-
if (explicit) return `inst-${explicit}`;
|
|
206
|
-
const base = deps.projectDir ?? env.CLAUDE_PROJECT_DIR ?? deps.cwd ?? process.cwd() ?? "";
|
|
207
|
-
const hash = createHash("sha256").update(base).digest("hex").slice(0, 12);
|
|
208
|
-
return `wd-${hash}`;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
153
|
// src/tandem/room-memory.ts
|
|
212
|
-
import
|
|
154
|
+
import fs from "fs";
|
|
213
155
|
import os from "os";
|
|
214
156
|
import path from "path";
|
|
215
157
|
function defaultKojeeDir() {
|
|
@@ -221,7 +163,7 @@ function seatedRoomsPath(key, dir = defaultKojeeDir()) {
|
|
|
221
163
|
function readSeatedRooms(key, dir = defaultKojeeDir()) {
|
|
222
164
|
let raw;
|
|
223
165
|
try {
|
|
224
|
-
raw =
|
|
166
|
+
raw = fs.readFileSync(seatedRoomsPath(key, dir), "utf8");
|
|
225
167
|
} catch {
|
|
226
168
|
return [];
|
|
227
169
|
}
|
|
@@ -233,17 +175,17 @@ function readSeatedRooms(key, dir = defaultKojeeDir()) {
|
|
|
233
175
|
}
|
|
234
176
|
}
|
|
235
177
|
function hasSeatedRoomsFile(key, dir = defaultKojeeDir()) {
|
|
236
|
-
return
|
|
178
|
+
return fs.existsSync(seatedRoomsPath(key, dir));
|
|
237
179
|
}
|
|
238
180
|
function seedSeatedRooms(key, rooms, dir = defaultKojeeDir()) {
|
|
239
181
|
writeSeatedRooms(key, [...new Set(rooms)], dir);
|
|
240
182
|
}
|
|
241
183
|
function writeSeatedRooms(key, rooms, dir) {
|
|
242
|
-
|
|
184
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
243
185
|
secureDir(dir);
|
|
244
186
|
const filePath = seatedRoomsPath(key, dir);
|
|
245
187
|
const body = { schema: 1, rooms, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
246
|
-
|
|
188
|
+
fs.writeFileSync(filePath, JSON.stringify(body, null, 2), { mode: 384 });
|
|
247
189
|
secureFile(filePath);
|
|
248
190
|
}
|
|
249
191
|
function addSeatedRoom(key, tandemId, dir = defaultKojeeDir()) {
|
|
@@ -294,21 +236,21 @@ async function startProxy(config) {
|
|
|
294
236
|
const keystorePath = config.keystorePath || DEFAULT_KEYSTORE_PATH;
|
|
295
237
|
const adapter = await selectAdapter();
|
|
296
238
|
console.error(`[kojee-mcp] Starting proxy for ${config.url} (runtime=${adapter.runtime})`);
|
|
239
|
+
const ccPid = await findClaudeAncestorPid();
|
|
240
|
+
const { instanceKey } = await applyStableSessionId(config.token, { ccPid });
|
|
297
241
|
const { registry, gateway } = await enrollAndDiscover(config, keystorePath);
|
|
298
242
|
console.error(
|
|
299
243
|
`[kojee-mcp] Ready \u2014 ${registry.toolCount} tools available from ${config.url}`
|
|
300
244
|
);
|
|
301
|
-
const ccPid = await findClaudeAncestorPid();
|
|
302
|
-
const instanceKey = resolveInstanceKey({ ccPid });
|
|
303
245
|
const roomMemory = {
|
|
304
246
|
hasMemory: () => hasSeatedRoomsFile(instanceKey),
|
|
305
247
|
read: () => readSeatedRooms(instanceKey),
|
|
306
248
|
seed: (rooms) => seedSeatedRooms(instanceKey, rooms)
|
|
307
249
|
};
|
|
308
250
|
const recordRooms = parseTandemsConfig(process.env["KOJEE_TANDEMS"]).mode === "auto-local";
|
|
309
|
-
if (
|
|
251
|
+
if (instanceKey.startsWith("wd-")) {
|
|
310
252
|
console.error(
|
|
311
|
-
"[kojee-mcp]
|
|
253
|
+
"[kojee-mcp] degraded per-window fidelity: no per-window session id \u2014 two concurrent windows in the same project dir share ONE seat" + (recordRooms ? " and one room-memory file" : "") + ". Set KOJEE_INSTANCE=<unique-per-window> to keep them distinct."
|
|
312
254
|
);
|
|
313
255
|
}
|
|
314
256
|
let activeStreamHandle = null;
|
|
@@ -370,7 +312,7 @@ async function startProxy(config) {
|
|
|
370
312
|
}
|
|
371
313
|
console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
|
|
372
314
|
let server;
|
|
373
|
-
const { selectDelivery } = await import("./registry-
|
|
315
|
+
const { selectDelivery } = await import("./registry-TGALQP6M.js");
|
|
374
316
|
const delivery = selectDelivery(adapter.runtime, {
|
|
375
317
|
supportsChannels: adapter.supportsChannels
|
|
376
318
|
});
|
|
@@ -445,7 +387,7 @@ async function enrollAndDiscover(config, keystorePath, isRetry = false) {
|
|
|
445
387
|
"[kojee-mcp] Auth failed, attempting recovery with fresh enrollment..."
|
|
446
388
|
);
|
|
447
389
|
try {
|
|
448
|
-
if (
|
|
390
|
+
if (fs2.existsSync(keystorePath)) fs2.unlinkSync(keystorePath);
|
|
449
391
|
} catch (unlinkErr) {
|
|
450
392
|
console.error("[kojee-mcp] Could not remove stale keystore:", unlinkErr);
|
|
451
393
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
2
|
+
createDPoPProof,
|
|
3
|
+
getSessionId
|
|
4
|
+
} from "./chunk-NR4Y54OL.js";
|
|
5
5
|
|
|
6
6
|
// src/tandem/event-stream.ts
|
|
7
7
|
var STALE_FLOOR_MS = 9e4;
|
|
@@ -180,7 +180,7 @@ async function openStream(opts, controller, sinceValue) {
|
|
|
180
180
|
headers: {
|
|
181
181
|
Authorization: `DPoP ${opts.token}`,
|
|
182
182
|
DPoP: proof,
|
|
183
|
-
"Mcp-Session-Id":
|
|
183
|
+
"Mcp-Session-Id": getSessionId(),
|
|
184
184
|
Accept: "text/event-stream"
|
|
185
185
|
},
|
|
186
186
|
signal: controller.signal
|
|
@@ -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
|
};
|
package/dist/cli.js
CHANGED
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
} from "./chunk-OGHDTFAX.js";
|
|
5
5
|
import {
|
|
6
6
|
startProxy
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3H3TL34J.js";
|
|
8
8
|
import "./chunk-XXFVWP6H.js";
|
|
9
9
|
import {
|
|
10
10
|
pairedConfigPath
|
|
11
11
|
} from "./chunk-YH27B6SW.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-IMOEZ4NJ.js";
|
|
13
13
|
import "./chunk-JXMVZEQ7.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-NR4Y54OL.js";
|
|
15
15
|
import {
|
|
16
16
|
defaultPairedKeystorePath,
|
|
17
17
|
deriveKeystorePath
|
|
@@ -19,9 +19,9 @@ import {
|
|
|
19
19
|
import "./chunk-BLEGIR35.js";
|
|
20
20
|
import {
|
|
21
21
|
VERSION
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-CM3EKMDD.js";
|
|
23
23
|
import "./chunk-X672ZN7V.js";
|
|
24
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-2OLXXOT3.js";
|
|
25
25
|
import "./chunk-VHKPWUX7.js";
|
|
26
26
|
import "./chunk-YKW54DKF.js";
|
|
27
27
|
|
|
@@ -79,7 +79,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
79
79
|
program.command("send <tandem_id>").description(
|
|
80
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)."
|
|
81
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) => {
|
|
82
|
-
const { runSendCli } = await import("./send-cli-
|
|
82
|
+
const { runSendCli } = await import("./send-cli-RH7D4JDP.js");
|
|
83
83
|
const { exitCode, envelope } = await runSendCli({
|
|
84
84
|
tandemId,
|
|
85
85
|
body: opts.body,
|
|
@@ -90,7 +90,7 @@ program.command("send <tandem_id>").description(
|
|
|
90
90
|
process.exit(exitCode);
|
|
91
91
|
});
|
|
92
92
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
93
|
-
const { runTail } = await import("./tail-stream-
|
|
93
|
+
const { runTail } = await import("./tail-stream-5DAVRQYK.js");
|
|
94
94
|
try {
|
|
95
95
|
await runTail(filePath);
|
|
96
96
|
} catch (err) {
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
sanitizeDisplayname,
|
|
7
7
|
serializeCursorMap,
|
|
8
8
|
startEventStream
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-74XFVX6Z.js";
|
|
10
|
+
import "./chunk-NR4Y54OL.js";
|
|
11
11
|
export {
|
|
12
12
|
UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
13
13
|
createAdaptiveWatchdog,
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTandemIds,
|
|
3
3
|
startProxy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3H3TL34J.js";
|
|
5
5
|
import "./chunk-XXFVWP6H.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-IMOEZ4NJ.js";
|
|
7
7
|
import "./chunk-JXMVZEQ7.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-NR4Y54OL.js";
|
|
9
9
|
import "./chunk-CH32ELFX.js";
|
|
10
10
|
import "./chunk-BLEGIR35.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-CM3EKMDD.js";
|
|
12
12
|
import "./chunk-X672ZN7V.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-2OLXXOT3.js";
|
|
14
14
|
import "./chunk-VHKPWUX7.js";
|
|
15
15
|
import "./chunk-YKW54DKF.js";
|
|
16
16
|
export {
|
package/dist/lib.d.ts
CHANGED
|
@@ -438,6 +438,49 @@ interface ResubscribeOptions {
|
|
|
438
438
|
*/
|
|
439
439
|
declare function resubscribeMemberships(opts: ResubscribeOptions): Promise<number>;
|
|
440
440
|
|
|
441
|
+
interface StableSessionDeps {
|
|
442
|
+
/**
|
|
443
|
+
* The Claude Code ancestor PID, when the caller already has it (the daemon).
|
|
444
|
+
* Pass `null` to SKIP the ancestry read (non-CC contexts, tests) — resolution
|
|
445
|
+
* then falls to KOJEE_INSTANCE → working-dir hash. Omit to resolve ancestry.
|
|
446
|
+
*/
|
|
447
|
+
ccPid?: number | null;
|
|
448
|
+
/**
|
|
449
|
+
* An explicit instanceKey — bypasses ancestry AND resolveInstanceKey entirely.
|
|
450
|
+
* Used where the window identity is known out-of-band (an embedder's account
|
|
451
|
+
* key, tests).
|
|
452
|
+
*/
|
|
453
|
+
instanceKey?: string;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* §7 — install the STABLE Mcp-Session-Id (D1) for THIS process, the one DRY way
|
|
457
|
+
* every outbound construction site does it: the daemon (index.ts), the `send`
|
|
458
|
+
* sub-command, and embedding plugins (OpenClaw). Same (token, instanceKey) →
|
|
459
|
+
* same id → the backend converges on ONE seat per (credential, window) across
|
|
460
|
+
* restarts (no phantom seats; reaper retired).
|
|
461
|
+
*
|
|
462
|
+
* instanceKey precedence: an explicit `deps.instanceKey` wins; otherwise it's
|
|
463
|
+
* resolved by {@link resolveInstanceKey} from `deps.ccPid` (or a fresh ancestry
|
|
464
|
+
* read when `ccPid` is omitted) → CC session id, else KOJEE_INSTANCE, else a
|
|
465
|
+
* working-dir hash.
|
|
466
|
+
*
|
|
467
|
+
* Returns the resolved instanceKey + the installed sessionId so callers that
|
|
468
|
+
* also need the key (room-memory, discovery) don't resolve it twice.
|
|
469
|
+
*/
|
|
470
|
+
declare function applyStableSessionId(token: string, deps?: StableSessionDeps): Promise<{
|
|
471
|
+
instanceKey: string;
|
|
472
|
+
sessionId: string;
|
|
473
|
+
}>;
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* D1 — the STABLE Mcp-Session-Id: deterministic per (token, instanceKey),
|
|
477
|
+
* one-way + opaque. Same inputs → same id across proxy restarts, so the backend
|
|
478
|
+
* resolves the SAME Session + seat (no phantom seats, reaper retired). Computed
|
|
479
|
+
* at proxy startup where the gateway token + instanceKey (resolveInstanceKey)
|
|
480
|
+
* are known, in place of the random ulid() above.
|
|
481
|
+
*/
|
|
482
|
+
declare function deriveStableSessionId(token: string, instanceKey: string): string;
|
|
483
|
+
|
|
441
484
|
/**
|
|
442
485
|
* Flat tool registry — fetches all Kojee tools with full schemas on startup
|
|
443
486
|
* and registers them directly with the MCP server. No discovery indirection,
|
|
@@ -604,4 +647,4 @@ interface WakeDelivery {
|
|
|
604
647
|
health?(): DeliveryHealth;
|
|
605
648
|
}
|
|
606
649
|
|
|
607
|
-
export { AuthModule, type DeliveryHealth, type EventLog, type EventStreamOptions, GatewayClient, type HarnessAdapter, type PairedConfig, ProxyConfig, type Runtime, type StartedDelivery, type StreamHandle, type StreamLike, type StreamState, type TandemEvent, ToolCallResult, type WakeDelivery, type WakeDeliveryContext, type WebhookSink, createDPoPProof, defaultPairedKeystorePath, deriveKeystorePath, generateES256KeyPair, loadKeystore, loadPairedConfig, normalizeBackendEvent, pairedConfigPath, resubscribeMemberships, sanitizeDisplayname, saveKeystore, startEventStream };
|
|
650
|
+
export { AuthModule, type DeliveryHealth, type EventLog, type EventStreamOptions, GatewayClient, type HarnessAdapter, type PairedConfig, ProxyConfig, type Runtime, type StableSessionDeps, type StartedDelivery, type StreamHandle, type StreamLike, type StreamState, type TandemEvent, ToolCallResult, type WakeDelivery, type WakeDeliveryContext, type WebhookSink, applyStableSessionId, createDPoPProof, defaultPairedKeystorePath, deriveKeystorePath, deriveStableSessionId, generateES256KeyPair, loadKeystore, loadPairedConfig, normalizeBackendEvent, pairedConfigPath, resubscribeMemberships, sanitizeDisplayname, saveKeystore, startEventStream };
|
package/dist/lib.js
CHANGED
|
@@ -5,20 +5,22 @@ import {
|
|
|
5
5
|
normalizeBackendEvent,
|
|
6
6
|
sanitizeDisplayname,
|
|
7
7
|
startEventStream
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-74XFVX6Z.js";
|
|
9
9
|
import {
|
|
10
10
|
loadPairedConfig,
|
|
11
11
|
pairedConfigPath
|
|
12
12
|
} from "./chunk-YH27B6SW.js";
|
|
13
13
|
import {
|
|
14
|
-
GatewayClient
|
|
15
|
-
|
|
14
|
+
GatewayClient,
|
|
15
|
+
applyStableSessionId
|
|
16
|
+
} from "./chunk-IMOEZ4NJ.js";
|
|
16
17
|
import {
|
|
17
18
|
AuthModule
|
|
18
19
|
} from "./chunk-JXMVZEQ7.js";
|
|
19
20
|
import {
|
|
20
|
-
createDPoPProof
|
|
21
|
-
|
|
21
|
+
createDPoPProof,
|
|
22
|
+
deriveStableSessionId
|
|
23
|
+
} from "./chunk-NR4Y54OL.js";
|
|
22
24
|
import {
|
|
23
25
|
defaultPairedKeystorePath,
|
|
24
26
|
deriveKeystorePath,
|
|
@@ -27,13 +29,16 @@ import {
|
|
|
27
29
|
saveKeystore
|
|
28
30
|
} from "./chunk-CH32ELFX.js";
|
|
29
31
|
import "./chunk-BLEGIR35.js";
|
|
30
|
-
import "./chunk-
|
|
32
|
+
import "./chunk-2OLXXOT3.js";
|
|
33
|
+
import "./chunk-VHKPWUX7.js";
|
|
31
34
|
export {
|
|
32
35
|
AuthModule,
|
|
33
36
|
GatewayClient,
|
|
37
|
+
applyStableSessionId,
|
|
34
38
|
createDPoPProof,
|
|
35
39
|
defaultPairedKeystorePath,
|
|
36
40
|
deriveKeystorePath,
|
|
41
|
+
deriveStableSessionId,
|
|
37
42
|
generateES256KeyPair,
|
|
38
43
|
loadKeystore,
|
|
39
44
|
loadPairedConfig,
|
|
@@ -4,9 +4,9 @@ import "./chunk-LSUB6QMP.js";
|
|
|
4
4
|
import {
|
|
5
5
|
claudeCodeAdapter
|
|
6
6
|
} from "./chunk-XXFVWP6H.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-CM3EKMDD.js";
|
|
8
8
|
import "./chunk-X672ZN7V.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-2OLXXOT3.js";
|
|
10
10
|
|
|
11
11
|
// src/delivery/lib/fanout.ts
|
|
12
12
|
async function deliverEvent(event, sinks) {
|
|
@@ -56,7 +56,7 @@ function createClaudeCodeDelivery() {
|
|
|
56
56
|
name: "claude-code",
|
|
57
57
|
async start(ctx) {
|
|
58
58
|
const { EventQueue } = await import("./event-queue-5YVJFR3E.js");
|
|
59
|
-
const { startHookServer } = await import("./hook-server-
|
|
59
|
+
const { startHookServer } = await import("./hook-server-T2Z444OV.js");
|
|
60
60
|
const {
|
|
61
61
|
writeDiscoveryByKey,
|
|
62
62
|
cleanupDiscoveryByKey,
|
|
@@ -66,8 +66,8 @@ function createClaudeCodeDelivery() {
|
|
|
66
66
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
67
67
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
68
68
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
69
|
-
const { startEventStream } = await import("./event-stream-
|
|
70
|
-
const { createMcpServer } = await import("./server-
|
|
69
|
+
const { startEventStream } = await import("./event-stream-XX5EZ6HN.js");
|
|
70
|
+
const { createMcpServer } = await import("./server-LBVEDIXP.js");
|
|
71
71
|
const { deriveDiscoveryKey } = await import("./ancestry-ONFBQEP5.js");
|
|
72
72
|
sweepStaleDiscovery();
|
|
73
73
|
sweepStaleEventLogs();
|
|
@@ -234,8 +234,8 @@ function createWebhookDelivery(name) {
|
|
|
234
234
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
235
235
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
236
236
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
237
|
-
const { startEventStream } = await import("./event-stream-
|
|
238
|
-
const { createMcpServer } = await import("./server-
|
|
237
|
+
const { startEventStream } = await import("./event-stream-XX5EZ6HN.js");
|
|
238
|
+
const { createMcpServer } = await import("./server-LBVEDIXP.js");
|
|
239
239
|
sweepStaleEventLogs();
|
|
240
240
|
eventLog = startEventLog({
|
|
241
241
|
key: ctx.instanceKey,
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
|
+
executeJoin,
|
|
2
3
|
executeSend,
|
|
3
4
|
parseSendRequest,
|
|
4
5
|
sendFailure
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DXJ6QLSJ.js";
|
|
6
7
|
import {
|
|
7
8
|
loadPairedConfig
|
|
8
9
|
} from "./chunk-YH27B6SW.js";
|
|
9
10
|
import {
|
|
10
|
-
GatewayClient
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
GatewayClient,
|
|
12
|
+
applyStableSessionId
|
|
13
|
+
} from "./chunk-IMOEZ4NJ.js";
|
|
14
|
+
import "./chunk-NR4Y54OL.js";
|
|
13
15
|
import {
|
|
14
16
|
loadKeystore
|
|
15
17
|
} from "./chunk-CH32ELFX.js";
|
|
16
18
|
import "./chunk-BLEGIR35.js";
|
|
17
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-2OLXXOT3.js";
|
|
20
|
+
import "./chunk-VHKPWUX7.js";
|
|
18
21
|
|
|
19
22
|
// src/tandem/send-cli.ts
|
|
20
23
|
import os from "os";
|
|
21
24
|
import path from "path";
|
|
22
|
-
async function createPairedGateway(kojeeDir) {
|
|
25
|
+
async function createPairedGateway(kojeeDir, stableDeps = {}) {
|
|
23
26
|
const configPath = path.join(kojeeDir, "config.json");
|
|
24
27
|
const paired = loadPairedConfig(configPath);
|
|
25
28
|
if (!paired) {
|
|
@@ -28,6 +31,7 @@ async function createPairedGateway(kojeeDir) {
|
|
|
28
31
|
`no paired config at ${configPath} \u2014 run \`kojee-mcp pair <code> --url <broker>\` first`
|
|
29
32
|
);
|
|
30
33
|
}
|
|
34
|
+
await applyStableSessionId(paired.token, stableDeps);
|
|
31
35
|
const brokerUrl = paired.broker_url.replace(/\/+$/, "");
|
|
32
36
|
const keystorePath = path.join(kojeeDir, "keypair.json");
|
|
33
37
|
const keystore = await loadKeystore(keystorePath, brokerUrl).catch(() => null);
|
|
@@ -65,6 +69,10 @@ async function runSendCli(args, deps = {}) {
|
|
|
65
69
|
if (!resolution.ok) {
|
|
66
70
|
return { exitCode: 1, envelope: resolution };
|
|
67
71
|
}
|
|
72
|
+
const joined = await executeJoin(resolution.gateway, parsed.request.tandem_id);
|
|
73
|
+
if (!joined.ok && (joined.error === "not_member" || joined.error === "gateway_auth")) {
|
|
74
|
+
return { exitCode: 1, envelope: joined };
|
|
75
|
+
}
|
|
68
76
|
const envelope = await executeSend(resolution.gateway, parsed.request);
|
|
69
77
|
return { exitCode: envelope.ok ? 0 : 1, envelope };
|
|
70
78
|
}
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
createMcpServer,
|
|
4
4
|
executeToolCall,
|
|
5
5
|
startMcpServer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CM3EKMDD.js";
|
|
7
7
|
import "./chunk-X672ZN7V.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-2OLXXOT3.js";
|
|
9
9
|
export {
|
|
10
10
|
buildChannelInstructions,
|
|
11
11
|
createMcpServer,
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
import "./chunk-DO42NPNR.js";
|
|
8
8
|
import {
|
|
9
9
|
createAdaptiveWatchdog
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-74XFVX6Z.js";
|
|
11
|
+
import "./chunk-NR4Y54OL.js";
|
|
12
12
|
import "./chunk-BLEGIR35.js";
|
|
13
13
|
|
|
14
14
|
// src/tail-stream.ts
|