kojee-mcp 0.5.18 → 0.6.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/dist/{chunk-XIDCYUTK.js → chunk-ALZX3SGI.js} +2 -2
- package/dist/{chunk-TN45ULEB.js → chunk-BYV7CWNY.js} +6 -6
- package/dist/{chunk-CO73VGWM.js → chunk-DURRQDYI.js} +1 -1
- package/dist/{chunk-35XBRG3V.js → chunk-KFDZWNQO.js} +1 -1
- package/dist/{chunk-34IRTWP6.js → chunk-KH5YEJIM.js} +13 -55
- package/dist/{chunk-XXFVWP6H.js → chunk-N35X22G7.js} +1 -1
- package/dist/{chunk-DXJ6QLSJ.js → chunk-PKOBR7ZN.js} +1 -1
- package/dist/{chunk-2OLXXOT3.js → chunk-PPTKGWFF.js} +69 -3
- package/dist/{chunk-X672ZN7V.js → chunk-Q7T3WAST.js} +1 -1
- package/dist/{chunk-74XFVX6Z.js → chunk-XN3IC4B5.js} +17 -5
- package/dist/cli.js +71 -76
- package/dist/{codex-stop-hook-VY7DOMAG.js → codex-stop-hook-RWQSZTST.js} +2 -2
- package/dist/{doctor-DVPT2ZRH.js → doctor-WVX3WCYX.js} +3 -3
- package/dist/{doctor-codex-PA3WO6LR.js → doctor-codex-Z5M4SFBG.js} +1 -1
- package/dist/{event-log-VZD7NKYX.js → event-log-HOBO3ECB.js} +1 -1
- package/dist/{event-stream-XX5EZ6HN.js → event-stream-SYBUJ5EH.js} +1 -1
- package/dist/{hook-server-XK2NHLJV.js → hook-server-CWBH6PFL.js} +2 -2
- package/dist/index.js +6 -6
- package/dist/lib.d.ts +15 -11
- package/dist/lib.js +3 -4
- package/dist/{registry-LBWRSM5Z.js → registry-YLOFZDI2.js} +12 -12
- package/dist/{send-cli-T6RPZZQ4.js → send-cli-IYJF32PE.js} +3 -4
- package/dist/{server-LTFG4GMV.js → server-R24AVNTI.js} +3 -3
- package/dist/{wizard-Y3ULSO2C.js → setup-handler-7HOFAD7C.js} +115 -7
- package/dist/{stop-hook-YAVJ3EJD.js → stop-hook-TCPJFGFF.js} +2 -2
- package/dist/{tail-stream-5DAVRQYK.js → tail-stream-4IMXI44S.js} +24 -3
- package/package.json +2 -1
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
buildCatchUpNote,
|
|
3
3
|
buildMonitorSpawn,
|
|
4
4
|
buildReplyRecipe
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-Q7T3WAST.js";
|
|
6
6
|
import {
|
|
7
7
|
translateToolCallResult
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PPTKGWFF.js";
|
|
9
9
|
|
|
10
10
|
// src/server.ts
|
|
11
11
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
claudeCodeAdapter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-N35X22G7.js";
|
|
4
4
|
import {
|
|
5
5
|
GatewayClient,
|
|
6
6
|
applyStableSessionId
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KH5YEJIM.js";
|
|
8
8
|
import {
|
|
9
9
|
AuthModule
|
|
10
10
|
} from "./chunk-JXMVZEQ7.js";
|
|
11
11
|
import {
|
|
12
12
|
createMcpServer,
|
|
13
13
|
startMcpServer
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ALZX3SGI.js";
|
|
15
15
|
import {
|
|
16
16
|
findClaudeAncestorPid
|
|
17
17
|
} from "./chunk-VHKPWUX7.js";
|
|
@@ -186,9 +186,9 @@ async function startProxy(config) {
|
|
|
186
186
|
console.error(
|
|
187
187
|
`[kojee-mcp] Ready \u2014 ${registry.toolCount} tools available from ${config.url}`
|
|
188
188
|
);
|
|
189
|
-
if (instanceKey.startsWith("
|
|
189
|
+
if (instanceKey.startsWith("mint-")) {
|
|
190
190
|
console.error(
|
|
191
|
-
"[kojee-mcp]
|
|
191
|
+
"[kojee-mcp] no stable session identity \u2014 minted a fresh per-launch seat (re-seats on restart). Set KOJEE_INSTANCE=<stable-per-session-value> for a restart-stable seat (required for daemons like Hermes)."
|
|
192
192
|
);
|
|
193
193
|
}
|
|
194
194
|
let activeStreamHandle = null;
|
|
@@ -238,7 +238,7 @@ async function startProxy(config) {
|
|
|
238
238
|
}
|
|
239
239
|
console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
|
|
240
240
|
let server;
|
|
241
|
-
const { selectDelivery } = await import("./registry-
|
|
241
|
+
const { selectDelivery } = await import("./registry-YLOFZDI2.js");
|
|
242
242
|
const delivery = selectDelivery(adapter.runtime, {
|
|
243
243
|
supportsChannels: adapter.supportsChannels
|
|
244
244
|
});
|
|
@@ -35,7 +35,7 @@ function startEventLog(opts) {
|
|
|
35
35
|
const statusMaxBytes = opts.statusMaxBytes ?? DEFAULT_STATUS_MAX_BYTES;
|
|
36
36
|
fs.mkdirSync(dir, { recursive: true });
|
|
37
37
|
let filePath = path.join(dir, `kojee-events-${opts.key}.log`);
|
|
38
|
-
if (
|
|
38
|
+
if (livePathOwnedBySibling(filePath)) {
|
|
39
39
|
filePath = path.join(dir, `kojee-events-${opts.key}-${process.pid}.log`);
|
|
40
40
|
}
|
|
41
41
|
const statusPath = statusLogPath(filePath);
|
|
@@ -8,10 +8,7 @@ import {
|
|
|
8
8
|
translateHttpError,
|
|
9
9
|
translateJsonRpcError,
|
|
10
10
|
translateNetworkError
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import {
|
|
13
|
-
findClaudeAncestorPid
|
|
14
|
-
} from "./chunk-VHKPWUX7.js";
|
|
11
|
+
} from "./chunk-PPTKGWFF.js";
|
|
15
12
|
|
|
16
13
|
// src/gateway-client.ts
|
|
17
14
|
import crypto from "crypto";
|
|
@@ -107,8 +104,11 @@ var GatewayClient = class {
|
|
|
107
104
|
const body = await this.tryParseErrorBody(response);
|
|
108
105
|
const translated = translateHttpError(response.status, body?.error);
|
|
109
106
|
if (translated) return translated;
|
|
107
|
+
const detail = body?.error_description ?? body?.detail ?? body?.error ?? "<no detail>";
|
|
110
108
|
return {
|
|
111
|
-
content: [
|
|
109
|
+
content: [
|
|
110
|
+
{ type: "text", text: `Gateway error ${response.status}: ${detail}` }
|
|
111
|
+
],
|
|
112
112
|
isError: true
|
|
113
113
|
};
|
|
114
114
|
}
|
|
@@ -190,70 +190,28 @@ var GatewayClient = class {
|
|
|
190
190
|
};
|
|
191
191
|
|
|
192
192
|
// src/runtime/cc-session-id.ts
|
|
193
|
-
import
|
|
194
|
-
|
|
195
|
-
import { createHash } from "crypto";
|
|
196
|
-
var CC_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
197
|
-
function extractCcSessionId(raw) {
|
|
198
|
-
if (raw.includes("\0")) {
|
|
199
|
-
for (const entry of raw.split("\0")) {
|
|
200
|
-
if (entry.startsWith(`${CC_SESSION_ENV}=`)) {
|
|
201
|
-
const v = entry.slice(CC_SESSION_ENV.length + 1);
|
|
202
|
-
return v.length > 0 ? v : null;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
const re = new RegExp(`(?:^|\\s)${CC_SESSION_ENV}=([^\\s]+)`, "g");
|
|
208
|
-
let last = null;
|
|
209
|
-
let m;
|
|
210
|
-
while ((m = re.exec(raw)) !== null) last = m[1];
|
|
211
|
-
return last;
|
|
212
|
-
}
|
|
213
|
-
function defaultReadProcessEnvRaw(pid, platform) {
|
|
214
|
-
try {
|
|
215
|
-
if (platform === "linux") {
|
|
216
|
-
return fs.readFileSync(`/proc/${pid}/environ`, "utf8");
|
|
217
|
-
}
|
|
218
|
-
if (platform === "darwin") {
|
|
219
|
-
return execFileSync("ps", ["eww", "-p", String(pid), "-o", "command="], {
|
|
220
|
-
encoding: "utf8",
|
|
221
|
-
timeout: 2e3
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
} catch {
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
193
|
+
import { randomUUID } from "crypto";
|
|
194
|
+
var SESSION_ID_ENV_VARS = ["CLAUDE_CODE_SESSION_ID"];
|
|
229
195
|
function sanitizeKey(value) {
|
|
230
196
|
return value.replace(/[^A-Za-z0-9_-]/g, "");
|
|
231
197
|
}
|
|
232
198
|
function resolveInstanceKey(deps = {}) {
|
|
233
199
|
const env = deps.env ?? process.env;
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const reader = deps.readProcessEnvRaw ?? defaultReadProcessEnvRaw;
|
|
238
|
-
const raw = reader(ccPid, platform);
|
|
239
|
-
if (raw) {
|
|
240
|
-
const sid = sanitizeKey(extractCcSessionId(raw) ?? "");
|
|
241
|
-
if (sid) return sid;
|
|
242
|
-
}
|
|
200
|
+
for (const name of SESSION_ID_ENV_VARS) {
|
|
201
|
+
const sid = sanitizeKey((env[name] ?? "").trim());
|
|
202
|
+
if (sid) return sid;
|
|
243
203
|
}
|
|
244
204
|
const explicit = sanitizeKey((env.KOJEE_INSTANCE ?? "").trim());
|
|
245
205
|
if (explicit) return `inst-${explicit}`;
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
return `wd-${hash}`;
|
|
206
|
+
const mint = deps.randomUUID ?? randomUUID;
|
|
207
|
+
return `mint-${mint()}`;
|
|
249
208
|
}
|
|
250
209
|
|
|
251
210
|
// src/runtime/stable-session.ts
|
|
252
211
|
async function applyStableSessionId(token, deps = {}) {
|
|
253
212
|
let instanceKey = deps.instanceKey;
|
|
254
213
|
if (instanceKey === void 0) {
|
|
255
|
-
|
|
256
|
-
instanceKey = resolveInstanceKey({ ccPid });
|
|
214
|
+
instanceKey = resolveInstanceKey({ env: deps.env });
|
|
257
215
|
}
|
|
258
216
|
const sessionId = deriveStableSessionId(token, instanceKey);
|
|
259
217
|
setStableSessionId(sessionId);
|
|
@@ -33,7 +33,7 @@ function translateHttpError(status, errorCode, trigger) {
|
|
|
33
33
|
}
|
|
34
34
|
if (errorCode === "invalid_dpop_proof") {
|
|
35
35
|
return makeError(
|
|
36
|
-
"Authentication failed.
|
|
36
|
+
"Authentication failed (DPoP key rejected). Restart the proxy so it re-enrolls its key. If this persists, regenerate your gateway token."
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
if (errorCode === "key_enrollment_required") {
|
|
@@ -61,8 +61,71 @@ function translateHttpError(status, errorCode, trigger) {
|
|
|
61
61
|
}
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
|
+
function translateTandemNotActive(reason) {
|
|
65
|
+
switch (reason) {
|
|
66
|
+
// --- genuine inactive / reaped seat: re-joining is the right fix ---
|
|
67
|
+
case "not_a_member":
|
|
68
|
+
case "not_active_member":
|
|
69
|
+
return "You aren't an active member of that Tandem (the seat may have been reaped). Run tandem_join to reactivate it, then retry.";
|
|
70
|
+
// --- authentication: pairing problem, tandem_join won't help ---
|
|
71
|
+
case "unauthenticated":
|
|
72
|
+
return "Your proxy session isn't authenticated/paired with Kojee, so this call was rejected. Re-pair the proxy \u2014 tandem_join won't fix this.";
|
|
73
|
+
// --- existence / visibility ---
|
|
74
|
+
case "tandem_not_found":
|
|
75
|
+
return "That Tandem doesn't exist or isn't visible to you.";
|
|
76
|
+
// --- power / scope: you lack the required role ---
|
|
77
|
+
case "owner_only":
|
|
78
|
+
return "This action requires owner power in this Tandem, which you don't have.";
|
|
79
|
+
case "admin_only":
|
|
80
|
+
return "This action requires admin power in this Tandem, which you don't have.";
|
|
81
|
+
// --- capacity ---
|
|
82
|
+
case "member_cap_reached":
|
|
83
|
+
return "This Tandem is full (member cap reached); rejoining won't help \u2014 ask an admin to make room.";
|
|
84
|
+
// --- message-scoped ---
|
|
85
|
+
case "message_not_found":
|
|
86
|
+
return "That message doesn't exist in this Tandem (wrong id, or it was already redacted).";
|
|
87
|
+
case "redact_forbidden":
|
|
88
|
+
return "You can't redact that message \u2014 you're neither its author nor an admin of this Tandem.";
|
|
89
|
+
// --- targeting ---
|
|
90
|
+
case "target_principal_not_found":
|
|
91
|
+
return "The target principal isn't a member of this Tandem.";
|
|
92
|
+
// --- invitations ---
|
|
93
|
+
case "invitation_not_found":
|
|
94
|
+
return "That invitation doesn't exist, or it was already revoked or used.";
|
|
95
|
+
case "invite_required":
|
|
96
|
+
return "This Tandem requires an invite to join; ask an owner or admin to invite you.";
|
|
97
|
+
case "invite_invalid":
|
|
98
|
+
return "That invite token is invalid.";
|
|
99
|
+
case "invite_expired":
|
|
100
|
+
return "That invite has expired; request a fresh one.";
|
|
101
|
+
case "invite_exhausted":
|
|
102
|
+
return "That invite has no remaining uses; request a fresh one.";
|
|
103
|
+
case "invite_wrong_principal":
|
|
104
|
+
return "That invite was issued to a different principal and can't be used by you.";
|
|
105
|
+
// --- join requests / knocks ---
|
|
106
|
+
case "join_request_not_found":
|
|
107
|
+
return "That join request doesn't exist or was already decided.";
|
|
108
|
+
case "join_request_not_pending":
|
|
109
|
+
return "That join request was already decided (it's no longer pending).";
|
|
110
|
+
case "already_member":
|
|
111
|
+
return "You're already a member of that Tandem.";
|
|
112
|
+
case "already_requested":
|
|
113
|
+
return "You already have a pending join request for that Tandem.";
|
|
114
|
+
case "knock_required":
|
|
115
|
+
return "This Tandem requires a knock; request to join and wait for an admin to approve.";
|
|
116
|
+
case "knock_cooldown":
|
|
117
|
+
return "You're knocking too frequently; wait a bit before requesting to join again.";
|
|
118
|
+
// --- direct (1:1) Tandems ---
|
|
119
|
+
case "direct_tandem_immutable_membership":
|
|
120
|
+
case "direct_immutable":
|
|
121
|
+
return "This is a direct (1:1) Tandem; its membership can't be changed.";
|
|
122
|
+
// --- unknown token: surface the real reason verbatim, never the reaped string ---
|
|
123
|
+
default:
|
|
124
|
+
return reason ? `Tandem request refused: ${reason}.` : "Tandem request refused.";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
64
127
|
var TANDEM_ERROR_MESSAGES = {
|
|
65
|
-
[-32001]: () =>
|
|
128
|
+
[-32001]: (_data, message) => translateTandemNotActive((message ?? "").trim()),
|
|
66
129
|
[-32003]: () => "This Tandem is hardened to owner-only membership; you can't join.",
|
|
67
130
|
[-32004]: () => "You aren't a member of that Tandem. Use tandem_join(join_link) first.",
|
|
68
131
|
[-32006]: (data) => {
|
|
@@ -91,7 +154,10 @@ function translateJsonRpcError(error) {
|
|
|
91
154
|
content: [
|
|
92
155
|
{
|
|
93
156
|
type: "text",
|
|
94
|
-
text: tandemMessage(
|
|
157
|
+
text: tandemMessage(
|
|
158
|
+
error.data,
|
|
159
|
+
msg
|
|
160
|
+
)
|
|
95
161
|
}
|
|
96
162
|
],
|
|
97
163
|
isError: true
|
|
@@ -13,7 +13,7 @@ function buildReplyRecipe(event) {
|
|
|
13
13
|
return `reply directly with tandem_send(tandem_id, ${SEND_BODY_PARAM}, reply_to=<msg>); fetch context only if needed via tandem_messages(tandem_id, since=cursor-1)`;
|
|
14
14
|
}
|
|
15
15
|
function buildCatchUpNote() {
|
|
16
|
-
return "
|
|
16
|
+
return "Cursors are allocated per-Tandem, and the event-log interleaves every subscribed room into one stream \u2014 so track the last `cursor=<n>` you saw keyed by the line's `tandem=<id>` field, and only treat a jump WITHIN the same tandem_id as a gap (a jump between lines from different rooms is normal interleaving, not a missed message). If the cursor jumps within one tandem, or the log resets (the proxy caps and truncates the messages log in place on overflow \u2014 a `status=rotated` line is recorded in the status sibling), you may have missed messages: catch up with tandem_messages(tandem_id, since=<last cursor you saw for THAT tandem>).";
|
|
17
17
|
}
|
|
18
18
|
function buildMonitorNudge(logPath) {
|
|
19
19
|
return `[kojee] Tandem events are being logged but no Monitor is reading them \u2014 you may be missing wake notifications. Spawn the watcher once: ${buildMonitorSpawn(logPath)}. Then ${buildReplyRecipe()}.`;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-NR4Y54OL.js";
|
|
5
5
|
|
|
6
6
|
// src/tandem/event-stream.ts
|
|
7
|
+
import { Agent } from "undici";
|
|
7
8
|
var STALE_FLOOR_MS = 9e4;
|
|
8
9
|
var STALE_INTERVAL_MULTIPLIER = 3;
|
|
9
10
|
var STALE_CHECK_INTERVAL_MS = 5e3;
|
|
@@ -71,6 +72,10 @@ function createBackoffController(options = {}) {
|
|
|
71
72
|
var LOG_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
72
73
|
async function startEventStream(opts) {
|
|
73
74
|
let stopped = false;
|
|
75
|
+
const streamDispatcher = new Agent({
|
|
76
|
+
bodyTimeout: UNDICI_DEFAULT_BODY_TIMEOUT_MS,
|
|
77
|
+
headersTimeout: UNDICI_DEFAULT_BODY_TIMEOUT_MS
|
|
78
|
+
});
|
|
74
79
|
const cursors = /* @__PURE__ */ new Map();
|
|
75
80
|
let currentController = new AbortController();
|
|
76
81
|
let connectGeneration = 0;
|
|
@@ -95,6 +100,7 @@ async function startEventStream(opts) {
|
|
|
95
100
|
currentController,
|
|
96
101
|
isReconnect,
|
|
97
102
|
state,
|
|
103
|
+
streamDispatcher,
|
|
98
104
|
(tandemId, cursor) => {
|
|
99
105
|
const prev = cursors.get(tandemId);
|
|
100
106
|
if (prev === void 0 || cursor > prev) cursors.set(tandemId, cursor);
|
|
@@ -125,6 +131,8 @@ async function startEventStream(opts) {
|
|
|
125
131
|
stopped = true;
|
|
126
132
|
state.connected = false;
|
|
127
133
|
currentController.abort();
|
|
134
|
+
void streamDispatcher.close().catch(() => {
|
|
135
|
+
});
|
|
128
136
|
});
|
|
129
137
|
handle.reconnect = () => {
|
|
130
138
|
if (stopped) return;
|
|
@@ -144,12 +152,12 @@ async function startEventStream(opts) {
|
|
|
144
152
|
});
|
|
145
153
|
return handle;
|
|
146
154
|
}
|
|
147
|
-
async function connectAndConsume(opts, cursors, controller, isReconnect, state, onCursor, onOpen) {
|
|
155
|
+
async function connectAndConsume(opts, cursors, controller, isReconnect, state, dispatcher, onCursor, onOpen) {
|
|
148
156
|
const mapSince = cursors.size > 0 ? serializeCursorMap(cursors) : null;
|
|
149
|
-
let res = await openStream(opts, controller, mapSince);
|
|
157
|
+
let res = await openStream(opts, controller, mapSince, dispatcher);
|
|
150
158
|
if (res.status === 400 && mapSince !== null) {
|
|
151
159
|
console.error("[event-stream] per-room ?since rejected (400) \u2014 falling back to bare cursor");
|
|
152
|
-
res = await openStream(opts, controller, String(Math.min(...cursors.values())));
|
|
160
|
+
res = await openStream(opts, controller, String(Math.min(...cursors.values())), dispatcher);
|
|
153
161
|
}
|
|
154
162
|
if (!res.ok) throw new Error(`SSE connect failed: ${res.status}`);
|
|
155
163
|
if (!res.body) throw new Error("SSE response has no body");
|
|
@@ -163,7 +171,7 @@ async function connectAndConsume(opts, cursors, controller, isReconnect, state,
|
|
|
163
171
|
await consumeSse(res.body, opts, controller, state, onCursor);
|
|
164
172
|
return true;
|
|
165
173
|
}
|
|
166
|
-
async function openStream(opts, controller, sinceValue) {
|
|
174
|
+
async function openStream(opts, controller, sinceValue, dispatcher) {
|
|
167
175
|
const params = new URLSearchParams();
|
|
168
176
|
if (sinceValue !== null) params.set("since", sinceValue);
|
|
169
177
|
const url = `${opts.brokerUrl}/api/v2/tandems/stream${params.toString() ? "?" + params.toString() : ""}`;
|
|
@@ -183,7 +191,11 @@ async function openStream(opts, controller, sinceValue) {
|
|
|
183
191
|
"Mcp-Session-Id": getSessionId(),
|
|
184
192
|
Accept: "text/event-stream"
|
|
185
193
|
},
|
|
186
|
-
signal: controller.signal
|
|
194
|
+
signal: controller.signal,
|
|
195
|
+
// Isolate this long-lived stream onto its own connection pool so it can
|
|
196
|
+
// never block tool-call POSTs on the shared global pool. The dispatcher is
|
|
197
|
+
// owned by startEventStream (per-stream lifecycle) and closed on teardown.
|
|
198
|
+
dispatcher
|
|
187
199
|
});
|
|
188
200
|
}
|
|
189
201
|
async function consumeSse(body, opts, controller, state, onCursor) {
|
package/dist/cli.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
} from "./chunk-OGHDTFAX.js";
|
|
5
5
|
import {
|
|
6
6
|
startProxy
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-BYV7CWNY.js";
|
|
8
|
+
import "./chunk-N35X22G7.js";
|
|
9
9
|
import {
|
|
10
10
|
pairedConfigPath
|
|
11
11
|
} from "./chunk-YH27B6SW.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-KH5YEJIM.js";
|
|
13
13
|
import "./chunk-JXMVZEQ7.js";
|
|
14
14
|
import "./chunk-NR4Y54OL.js";
|
|
15
15
|
import {
|
|
@@ -19,9 +19,9 @@ import {
|
|
|
19
19
|
import "./chunk-BLEGIR35.js";
|
|
20
20
|
import {
|
|
21
21
|
VERSION
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
22
|
+
} from "./chunk-ALZX3SGI.js";
|
|
23
|
+
import "./chunk-Q7T3WAST.js";
|
|
24
|
+
import "./chunk-PPTKGWFF.js";
|
|
25
25
|
import "./chunk-VHKPWUX7.js";
|
|
26
26
|
|
|
27
27
|
// src/cli.ts
|
|
@@ -44,7 +44,7 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
44
44
|
});
|
|
45
45
|
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
46
|
if (opts.type === "stop") {
|
|
47
|
-
const { runStopHook } = await import("./stop-hook-
|
|
47
|
+
const { runStopHook } = await import("./stop-hook-TCPJFGFF.js");
|
|
48
48
|
await runStopHook();
|
|
49
49
|
process.exit(0);
|
|
50
50
|
} else if (opts.type === "user-prompt-submit") {
|
|
@@ -52,7 +52,7 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
|
|
|
52
52
|
await runUserPromptSubmitHook();
|
|
53
53
|
process.exit(0);
|
|
54
54
|
} else if (opts.type === "codex-stop") {
|
|
55
|
-
const { runCodexStopHook } = await import("./codex-stop-hook-
|
|
55
|
+
const { runCodexStopHook } = await import("./codex-stop-hook-RWQSZTST.js");
|
|
56
56
|
await runCodexStopHook();
|
|
57
57
|
process.exit(0);
|
|
58
58
|
} else {
|
|
@@ -78,7 +78,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
78
78
|
program.command("send <tandem_id>").description(
|
|
79
79
|
"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
80
|
).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-
|
|
81
|
+
const { runSendCli } = await import("./send-cli-IYJF32PE.js");
|
|
82
82
|
const { exitCode, envelope } = await runSendCli({
|
|
83
83
|
tandemId,
|
|
84
84
|
body: opts.body,
|
|
@@ -89,7 +89,7 @@ program.command("send <tandem_id>").description(
|
|
|
89
89
|
process.exit(exitCode);
|
|
90
90
|
});
|
|
91
91
|
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-
|
|
92
|
+
const { runTail } = await import("./tail-stream-4IMXI44S.js");
|
|
93
93
|
try {
|
|
94
94
|
await runTail(filePath);
|
|
95
95
|
} catch (err) {
|
|
@@ -98,75 +98,70 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
98
98
|
}
|
|
99
99
|
});
|
|
100
100
|
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-
|
|
101
|
+
const { runDoctor } = await import("./doctor-WVX3WCYX.js");
|
|
102
102
|
const code = await runDoctor();
|
|
103
103
|
process.exit(code);
|
|
104
104
|
});
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
const {
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
} : {}
|
|
166
|
-
});
|
|
167
|
-
console.error(result.output);
|
|
168
|
-
process.exit(result.exitCode);
|
|
169
|
-
});
|
|
105
|
+
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 (writes ~/.kojee/config.json) before configuring").option("--webhook-url <url>", "Webhook receiver URL (codex/hermes/openclaw)").option("--webhook-secret <secret>", "Webhook HMAC secret (generated if omitted)").option(
|
|
107
|
+
"--webhook-signature-format <preset>",
|
|
108
|
+
'Signature preset: "github" = header X-Hub-Signature-256, value sha256=<hex> (default: bare hex in X-Kojee-Signature)'
|
|
109
|
+
).option(
|
|
110
|
+
"--webhook-signature-header <name>",
|
|
111
|
+
"Signature header name (default X-Kojee-Signature; overrides the preset's header)"
|
|
112
|
+
).option(
|
|
113
|
+
"--webhook-signature-prefix <prefix>",
|
|
114
|
+
"Literal string prepended to the hex digest (default empty; overrides the preset's prefix)"
|
|
115
|
+
).option("--uninstall", "Remove the kojee config for the chosen (or recorded) runtime");
|
|
116
|
+
}
|
|
117
|
+
function makeInstallAction(verb) {
|
|
118
|
+
return async (opts) => {
|
|
119
|
+
const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
|
|
120
|
+
const { runSetup, resolvePairCode } = await import("./setup-handler-7HOFAD7C.js");
|
|
121
|
+
const pairCode = resolvePairCode(opts);
|
|
122
|
+
const result = await runSetup({
|
|
123
|
+
verb,
|
|
124
|
+
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
|
125
|
+
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
126
|
+
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
127
|
+
...opts.token ? { token: opts.token } : {},
|
|
128
|
+
...opts.url ? { url: opts.url } : {},
|
|
129
|
+
...pairCode ? { pairCode } : {},
|
|
130
|
+
...opts.webhookUrl ? { webhookUrl: opts.webhookUrl } : {},
|
|
131
|
+
...opts.webhookSecret ? { webhookSecret: opts.webhookSecret } : {},
|
|
132
|
+
...opts.webhookSignatureFormat ? { webhookSignatureFormat: opts.webhookSignatureFormat } : {},
|
|
133
|
+
...opts.webhookSignatureHeader ? { webhookSignatureHeader: opts.webhookSignatureHeader } : {},
|
|
134
|
+
...opts.webhookSignaturePrefix !== void 0 ? { webhookSignaturePrefix: opts.webhookSignaturePrefix } : {},
|
|
135
|
+
...opts.uninstall ? { uninstall: true } : {},
|
|
136
|
+
interactive,
|
|
137
|
+
...interactive ? {
|
|
138
|
+
prompts: {
|
|
139
|
+
promptRuntime: promptRuntimeFromTty,
|
|
140
|
+
promptUrl: promptUrlFromTty,
|
|
141
|
+
promptAuth: promptAuthFromTty,
|
|
142
|
+
promptToken: promptTokenFromTty,
|
|
143
|
+
promptPairCode: promptPairCodeFromTty,
|
|
144
|
+
promptWebhookUrl: promptWebhookUrlFromTty
|
|
145
|
+
}
|
|
146
|
+
} : {}
|
|
147
|
+
});
|
|
148
|
+
for (const m of result.messages) console.error(m);
|
|
149
|
+
console.error(result.output);
|
|
150
|
+
process.exit(result.exitCode);
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
addInstallOptions(
|
|
154
|
+
program.command("init").description(
|
|
155
|
+
"Set up kojee for a runtime (claude-code | hermes | openclaw | codex). Interactive when stdin is a TTY; `--runtime <id>` for non-interactive/CI. Run after `kojee-mcp pair`."
|
|
156
|
+
),
|
|
157
|
+
"Target runtime: claude-code | hermes | openclaw | codex"
|
|
158
|
+
).action(makeInstallAction("init"));
|
|
159
|
+
addInstallOptions(
|
|
160
|
+
program.command("setup").description(
|
|
161
|
+
"One-line install: pair + write the runtime's MCP config + hooks in one shot (claude-code | codex). `--pair <code> --url <broker> --runtime <id>`, or the token form `--token <T> --url <U> --runtime <id>` for CI."
|
|
162
|
+
),
|
|
163
|
+
"Target runtime: claude-code | codex"
|
|
164
|
+
).action(makeInstallAction("setup"));
|
|
170
165
|
async function withReadline(fn) {
|
|
171
166
|
const readline = await import("readline/promises");
|
|
172
167
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
decideCodexStopHook,
|
|
5
5
|
defaultPeekPending,
|
|
6
6
|
runCodexStopHook
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KFDZWNQO.js";
|
|
8
8
|
import "./chunk-LSUB6QMP.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-Q7T3WAST.js";
|
|
10
10
|
export {
|
|
11
11
|
CODEX_PEEK_BUDGET_MS,
|
|
12
12
|
codexPendingMarkerPath,
|
|
@@ -2,7 +2,7 @@ import "./chunk-XLKGPGZT.js";
|
|
|
2
2
|
import {
|
|
3
3
|
monitorHeartbeatPath,
|
|
4
4
|
statusLogPath
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DURRQDYI.js";
|
|
6
6
|
import {
|
|
7
7
|
discoveryPathForKey,
|
|
8
8
|
readSessionDiscoveryByKey,
|
|
@@ -18,7 +18,7 @@ import "./chunk-BLEGIR35.js";
|
|
|
18
18
|
import {
|
|
19
19
|
buildMonitorSpawn,
|
|
20
20
|
buildReplyRecipe
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-Q7T3WAST.js";
|
|
22
22
|
import {
|
|
23
23
|
deriveDiscoveryKey,
|
|
24
24
|
findClaudeAncestorPid
|
|
@@ -328,7 +328,7 @@ function formatDoctorReport(report) {
|
|
|
328
328
|
async function runDoctor() {
|
|
329
329
|
const { readRecordedRuntime } = await import("./runtime-record-WO4IECM6.js");
|
|
330
330
|
if (readRecordedRuntime() === "codex") {
|
|
331
|
-
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-
|
|
331
|
+
const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-Z5M4SFBG.js");
|
|
332
332
|
const report2 = collectCodexDoctorReport();
|
|
333
333
|
console.error(formatCodexDoctorReport(report2));
|
|
334
334
|
return report2.verdict === "broken" ? 1 : 0;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTandemIds,
|
|
3
3
|
startProxy
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-BYV7CWNY.js";
|
|
5
|
+
import "./chunk-N35X22G7.js";
|
|
6
|
+
import "./chunk-KH5YEJIM.js";
|
|
7
7
|
import "./chunk-JXMVZEQ7.js";
|
|
8
8
|
import "./chunk-NR4Y54OL.js";
|
|
9
9
|
import "./chunk-CH32ELFX.js";
|
|
10
10
|
import "./chunk-BLEGIR35.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-ALZX3SGI.js";
|
|
12
|
+
import "./chunk-Q7T3WAST.js";
|
|
13
|
+
import "./chunk-PPTKGWFF.js";
|
|
14
14
|
import "./chunk-VHKPWUX7.js";
|
|
15
15
|
export {
|
|
16
16
|
listTandemIds,
|
package/dist/lib.d.ts
CHANGED
|
@@ -440,13 +440,16 @@ declare function resubscribeMemberships(opts: ResubscribeOptions): Promise<numbe
|
|
|
440
440
|
|
|
441
441
|
interface StableSessionDeps {
|
|
442
442
|
/**
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
443
|
+
* DEPRECATED / IGNORED. The resolver no longer reads process ancestry — it
|
|
444
|
+
* reads the runtime's session id from the proxy's OWN env (see
|
|
445
|
+
* {@link resolveInstanceKey}). Accepted only so callers that still pass
|
|
446
|
+
* `{ ccPid }` keep compiling; it has no effect on resolution.
|
|
446
447
|
*/
|
|
447
448
|
ccPid?: number | null;
|
|
449
|
+
/** Environment for {@link resolveInstanceKey} (defaults to `process.env`). Tests pin it. */
|
|
450
|
+
env?: NodeJS.ProcessEnv;
|
|
448
451
|
/**
|
|
449
|
-
* An explicit instanceKey — bypasses
|
|
452
|
+
* An explicit instanceKey — bypasses {@link resolveInstanceKey} entirely.
|
|
450
453
|
* Used where the window identity is known out-of-band (an embedder's account
|
|
451
454
|
* key, tests).
|
|
452
455
|
*/
|
|
@@ -459,10 +462,10 @@ interface StableSessionDeps {
|
|
|
459
462
|
* same id → the backend converges on ONE seat per (credential, window) across
|
|
460
463
|
* restarts (no phantom seats; reaper retired).
|
|
461
464
|
*
|
|
462
|
-
* instanceKey precedence: an explicit `deps.instanceKey` wins; otherwise
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
465
|
+
* instanceKey precedence: an explicit `deps.instanceKey` wins; otherwise
|
|
466
|
+
* {@link resolveInstanceKey} resolves it from the proxy's OWN env → the runtime
|
|
467
|
+
* session id (e.g. CLAUDE_CODE_SESSION_ID), else KOJEE_INSTANCE, else a minted
|
|
468
|
+
* per-launch id. (No process-ancestry read, no directory hash — both removed.)
|
|
466
469
|
*
|
|
467
470
|
* Returns the resolved instanceKey + the installed sessionId so callers that
|
|
468
471
|
* also need the key (room-memory, discovery) don't resolve it twice.
|
|
@@ -565,9 +568,10 @@ interface ToolCallHooks {
|
|
|
565
568
|
*/
|
|
566
569
|
interface WakeDeliveryContext {
|
|
567
570
|
/**
|
|
568
|
-
* This instance's stable key (#28): the
|
|
569
|
-
* KOJEE_INSTANCE
|
|
570
|
-
* event-log filename and other per-session
|
|
571
|
+
* This instance's stable key (#28): the runtime's own-env session id, else
|
|
572
|
+
* `inst-<KOJEE_INSTANCE>`, else a minted `mint-<uuid>` (see `resolveInstanceKey`).
|
|
573
|
+
* Used to derive the per-runtime event-log filename and other per-session
|
|
574
|
+
* artifacts.
|
|
571
575
|
*/
|
|
572
576
|
instanceKey: string;
|
|
573
577
|
/** The detected runtime label (drives capability + which delivery is built). */
|
package/dist/lib.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
normalizeBackendEvent,
|
|
6
6
|
sanitizeDisplayname,
|
|
7
7
|
startEventStream
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XN3IC4B5.js";
|
|
9
9
|
import {
|
|
10
10
|
loadPairedConfig,
|
|
11
11
|
pairedConfigPath
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
GatewayClient,
|
|
15
15
|
applyStableSessionId
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-KH5YEJIM.js";
|
|
17
17
|
import {
|
|
18
18
|
AuthModule
|
|
19
19
|
} from "./chunk-JXMVZEQ7.js";
|
|
@@ -29,8 +29,7 @@ import {
|
|
|
29
29
|
saveKeystore
|
|
30
30
|
} from "./chunk-CH32ELFX.js";
|
|
31
31
|
import "./chunk-BLEGIR35.js";
|
|
32
|
-
import "./chunk-
|
|
33
|
-
import "./chunk-VHKPWUX7.js";
|
|
32
|
+
import "./chunk-PPTKGWFF.js";
|
|
34
33
|
export {
|
|
35
34
|
AuthModule,
|
|
36
35
|
GatewayClient,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-KFDZWNQO.js";
|
|
2
2
|
import "./chunk-PHXO5P25.js";
|
|
3
3
|
import "./chunk-LSUB6QMP.js";
|
|
4
4
|
import {
|
|
5
5
|
claudeCodeAdapter
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-N35X22G7.js";
|
|
7
|
+
import "./chunk-ALZX3SGI.js";
|
|
8
|
+
import "./chunk-Q7T3WAST.js";
|
|
9
|
+
import "./chunk-PPTKGWFF.js";
|
|
10
10
|
|
|
11
11
|
// src/delivery/lib/fanout.ts
|
|
12
12
|
async function deliverEvent(event, sinks) {
|
|
@@ -91,18 +91,18 @@ function createClaudeCodeDelivery() {
|
|
|
91
91
|
name: "claude-code",
|
|
92
92
|
async start(ctx) {
|
|
93
93
|
const { EventQueue } = await import("./event-queue-5YVJFR3E.js");
|
|
94
|
-
const { startHookServer } = await import("./hook-server-
|
|
94
|
+
const { startHookServer } = await import("./hook-server-CWBH6PFL.js");
|
|
95
95
|
const {
|
|
96
96
|
writeDiscoveryByKey,
|
|
97
97
|
cleanupDiscoveryByKey,
|
|
98
98
|
sweepStaleDiscovery
|
|
99
99
|
} = await import("./session-discovery-FNMJGFPM.js");
|
|
100
|
-
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-
|
|
100
|
+
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-HOBO3ECB.js");
|
|
101
101
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
102
102
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
103
103
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
104
|
-
const { startEventStream } = await import("./event-stream-
|
|
105
|
-
const { createMcpServer } = await import("./server-
|
|
104
|
+
const { startEventStream } = await import("./event-stream-SYBUJ5EH.js");
|
|
105
|
+
const { createMcpServer } = await import("./server-R24AVNTI.js");
|
|
106
106
|
const { deriveDiscoveryKey } = await import("./ancestry-ONFBQEP5.js");
|
|
107
107
|
sweepStaleDiscovery();
|
|
108
108
|
sweepStaleEventLogs();
|
|
@@ -274,12 +274,12 @@ function createWebhookDelivery(name) {
|
|
|
274
274
|
return {
|
|
275
275
|
name,
|
|
276
276
|
async start(ctx) {
|
|
277
|
-
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-
|
|
277
|
+
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-HOBO3ECB.js");
|
|
278
278
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
279
279
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
280
280
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
281
|
-
const { startEventStream } = await import("./event-stream-
|
|
282
|
-
const { createMcpServer } = await import("./server-
|
|
281
|
+
const { startEventStream } = await import("./event-stream-SYBUJ5EH.js");
|
|
282
|
+
const { createMcpServer } = await import("./server-R24AVNTI.js");
|
|
283
283
|
sweepStaleEventLogs();
|
|
284
284
|
eventLog = startEventLog({
|
|
285
285
|
key: ctx.instanceKey,
|
|
@@ -3,21 +3,20 @@ import {
|
|
|
3
3
|
executeSend,
|
|
4
4
|
parseSendRequest,
|
|
5
5
|
sendFailure
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PKOBR7ZN.js";
|
|
7
7
|
import {
|
|
8
8
|
loadPairedConfig
|
|
9
9
|
} from "./chunk-YH27B6SW.js";
|
|
10
10
|
import {
|
|
11
11
|
GatewayClient,
|
|
12
12
|
applyStableSessionId
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KH5YEJIM.js";
|
|
14
14
|
import "./chunk-NR4Y54OL.js";
|
|
15
15
|
import {
|
|
16
16
|
loadKeystore
|
|
17
17
|
} from "./chunk-CH32ELFX.js";
|
|
18
18
|
import "./chunk-BLEGIR35.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-VHKPWUX7.js";
|
|
19
|
+
import "./chunk-PPTKGWFF.js";
|
|
21
20
|
|
|
22
21
|
// src/tandem/send-cli.ts
|
|
23
22
|
import os from "os";
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
createMcpServer,
|
|
4
4
|
executeToolCall,
|
|
5
5
|
startMcpServer
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-ALZX3SGI.js";
|
|
7
|
+
import "./chunk-Q7T3WAST.js";
|
|
8
|
+
import "./chunk-PPTKGWFF.js";
|
|
9
9
|
export {
|
|
10
10
|
buildChannelInstructions,
|
|
11
11
|
createMcpServer,
|
|
@@ -25,13 +25,16 @@ import {
|
|
|
25
25
|
resolveSignatureEmission,
|
|
26
26
|
resolveWebhookConfig
|
|
27
27
|
} from "./chunk-V5VZPYMZ.js";
|
|
28
|
+
import {
|
|
29
|
+
loadPairedConfig
|
|
30
|
+
} from "./chunk-YH27B6SW.js";
|
|
28
31
|
import {
|
|
29
32
|
secureFile
|
|
30
33
|
} from "./chunk-BLEGIR35.js";
|
|
31
34
|
import {
|
|
32
35
|
CODEX_LISTEN_CAP_MS,
|
|
33
36
|
buildWebhookReceiverNote
|
|
34
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-Q7T3WAST.js";
|
|
35
38
|
|
|
36
39
|
// src/wizard/wizard.ts
|
|
37
40
|
import crypto2 from "crypto";
|
|
@@ -824,11 +827,116 @@ registerBuiltinInstaller(
|
|
|
824
827
|
["pair-credential", "in-process-plugin"],
|
|
825
828
|
(o) => configureOpenclaw(o)
|
|
826
829
|
);
|
|
830
|
+
|
|
831
|
+
// src/wizard/setup-handler.ts
|
|
832
|
+
var SETUP_SUPPORTED_RUNTIMES = ["claude-code", "codex"];
|
|
833
|
+
function isSetupRuntime(value) {
|
|
834
|
+
return SETUP_SUPPORTED_RUNTIMES.includes(value);
|
|
835
|
+
}
|
|
836
|
+
function resolvePairCode(opts) {
|
|
837
|
+
return opts.pair ?? opts.pairCode;
|
|
838
|
+
}
|
|
839
|
+
async function defaultRunPair(args) {
|
|
840
|
+
const { runPair } = await import("./pair-P4ILCMT7.js");
|
|
841
|
+
const { pairedConfigPath } = await import("./paired-config-JTFLHMZ2.js");
|
|
842
|
+
const { defaultPairedKeystorePath } = await import("./keystore-XLEV3FL5.js");
|
|
843
|
+
return runPair({
|
|
844
|
+
code: args.code,
|
|
845
|
+
url: args.url,
|
|
846
|
+
keystorePath: defaultPairedKeystorePath(),
|
|
847
|
+
configPath: pairedConfigPath()
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
async function runSetup(opts) {
|
|
851
|
+
const messages = [];
|
|
852
|
+
const requested = opts.runtime ?? "claude-code";
|
|
853
|
+
if (opts.verb === "setup" && opts.runtime !== void 0 && !isSetupRuntime(opts.runtime)) {
|
|
854
|
+
const supported = SETUP_SUPPORTED_RUNTIMES.join(", ");
|
|
855
|
+
const output = isWizardRuntime(opts.runtime) ? `\`setup\` does not support --runtime ${opts.runtime} yet (coming soon). Supported by \`setup\`: ${supported}. For ${opts.runtime}, use \`kojee-mcp init --runtime ${opts.runtime}\` for now.` : `Unknown --runtime "${opts.runtime}". \`setup\` supports: ${supported}. (For hermes/openclaw use \`kojee-mcp init\`.)`;
|
|
856
|
+
return { runtime: requested, messages, output, exitCode: 2 };
|
|
857
|
+
}
|
|
858
|
+
if (opts.token !== void 0 && (opts.url === void 0 || opts.url.trim() === "") && !opts.uninstall) {
|
|
859
|
+
return {
|
|
860
|
+
runtime: requested,
|
|
861
|
+
messages,
|
|
862
|
+
output: "--token requires --url (the broker URL the Kojee UI gave you). Re-run with --url <broker-url>. Nothing was written.",
|
|
863
|
+
exitCode: 2
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
const url = opts.url ? opts.url.replace(/\/+$/, "") : void 0;
|
|
867
|
+
if (opts.pairCode && !opts.uninstall) {
|
|
868
|
+
const pair = opts.runPair ?? defaultRunPair;
|
|
869
|
+
const pairUrl = url ?? DEFAULT_BROKER_URL;
|
|
870
|
+
try {
|
|
871
|
+
const r = await pair({ code: opts.pairCode, url: pairUrl });
|
|
872
|
+
messages.push(r.message);
|
|
873
|
+
} catch (err) {
|
|
874
|
+
return {
|
|
875
|
+
runtime: requested,
|
|
876
|
+
messages,
|
|
877
|
+
output: `Pairing failed: ${err.message}`,
|
|
878
|
+
exitCode: 1
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
const loadCfg = opts.loadPairedConfig ?? loadPairedConfig;
|
|
883
|
+
const hasCredential = loadCfg() !== null || opts.token !== void 0;
|
|
884
|
+
if (!hasCredential && !opts.interactive && !opts.uninstall) {
|
|
885
|
+
return {
|
|
886
|
+
runtime: requested,
|
|
887
|
+
messages,
|
|
888
|
+
output: "Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run \u2014 or pass --pair <code> (or --token <T> --url <U>), or run in a terminal for the guided wizard.",
|
|
889
|
+
exitCode: 1
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
const result = await runWizard({
|
|
893
|
+
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
|
894
|
+
...opts.uninstall ? { uninstall: true } : {},
|
|
895
|
+
...opts.configPath ? { configPath: opts.configPath } : {},
|
|
896
|
+
...opts.hooksPath ? { hooksPath: opts.hooksPath } : {},
|
|
897
|
+
// Token mode (token + url) threads --token/--url into the written config.
|
|
898
|
+
...opts.token ? { token: opts.token } : {},
|
|
899
|
+
...url ? { url } : {},
|
|
900
|
+
...opts.webhookUrl ? { webhookUrl: opts.webhookUrl } : {},
|
|
901
|
+
...opts.webhookSecret ? { webhookSecret: opts.webhookSecret } : {},
|
|
902
|
+
...opts.webhookSignatureFormat ? { webhookSignatureFormat: opts.webhookSignatureFormat } : {},
|
|
903
|
+
...opts.webhookSignatureHeader ? { webhookSignatureHeader: opts.webhookSignatureHeader } : {},
|
|
904
|
+
...opts.webhookSignaturePrefix !== void 0 ? { webhookSignaturePrefix: opts.webhookSignaturePrefix } : {},
|
|
905
|
+
interactive: opts.interactive,
|
|
906
|
+
...opts.interactive && opts.prompts ? opts.prompts : {}
|
|
907
|
+
});
|
|
908
|
+
if (opts.verb === "setup" && result.exitCode === 0 && !opts.uninstall) {
|
|
909
|
+
return {
|
|
910
|
+
runtime: result.runtime,
|
|
911
|
+
messages,
|
|
912
|
+
output: crispSetupSummary(result.runtime, opts, url),
|
|
913
|
+
exitCode: 0
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
return { runtime: result.runtime, messages, output: result.output, exitCode: result.exitCode };
|
|
917
|
+
}
|
|
918
|
+
function crispSetupSummary(runtime, opts, url) {
|
|
919
|
+
const authLine = opts.token ? ` Auth: token mode (--token/--url${url ? `, ${url}` : ""})` : " Auth: paired credentials (~/.kojee/config.json)";
|
|
920
|
+
const lines = [];
|
|
921
|
+
if (runtime === "codex") {
|
|
922
|
+
lines.push("\u2713 kojee is set up for Codex.");
|
|
923
|
+
lines.push(` MCP config: ${opts.configPath ?? "~/.codex/config.toml"} ([mcp_servers.kojee])`);
|
|
924
|
+
lines.push(` Stop hook: ${opts.hooksPath ?? "~/.codex/hooks.json"} (codex-stop)`);
|
|
925
|
+
lines.push(authLine);
|
|
926
|
+
lines.push("");
|
|
927
|
+
lines.push("Next: restart Codex to connect.");
|
|
928
|
+
} else {
|
|
929
|
+
lines.push("\u2713 kojee is set up for Claude Code.");
|
|
930
|
+
lines.push(` MCP config: ${opts.configPath ?? "~/.claude.json"} (mcpServers.kojee)`);
|
|
931
|
+
lines.push(` Hooks: ${opts.hooksPath ?? "~/.claude/settings.json"} (Stop + UserPromptSubmit)`);
|
|
932
|
+
lines.push(authLine);
|
|
933
|
+
lines.push("");
|
|
934
|
+
lines.push("Next: restart Claude Code to connect.");
|
|
935
|
+
}
|
|
936
|
+
return lines.join("\n");
|
|
937
|
+
}
|
|
827
938
|
export {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
resolveRuntime,
|
|
832
|
-
resolveWizardWebhook,
|
|
833
|
-
runWizard
|
|
939
|
+
SETUP_SUPPORTED_RUNTIMES,
|
|
940
|
+
resolvePairCode,
|
|
941
|
+
runSetup
|
|
834
942
|
};
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
monitorHeartbeatPath,
|
|
10
10
|
nudgeSentinelPath
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DURRQDYI.js";
|
|
12
12
|
import {
|
|
13
13
|
readSessionDiscoveryByKey
|
|
14
14
|
} from "./chunk-DO42NPNR.js";
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import "./chunk-BLEGIR35.js";
|
|
19
19
|
import {
|
|
20
20
|
buildMonitorNudge
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-Q7T3WAST.js";
|
|
22
22
|
import {
|
|
23
23
|
deriveDiscoveryKey,
|
|
24
24
|
findClaudeAncestorPid
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
STATUS_LINE_PREFIX,
|
|
4
4
|
monitorHeartbeatPath,
|
|
5
5
|
statusLogPath
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DURRQDYI.js";
|
|
7
7
|
import "./chunk-DO42NPNR.js";
|
|
8
8
|
import {
|
|
9
9
|
createAdaptiveWatchdog
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XN3IC4B5.js";
|
|
11
11
|
import "./chunk-NR4Y54OL.js";
|
|
12
12
|
import "./chunk-BLEGIR35.js";
|
|
13
13
|
|
|
@@ -15,6 +15,9 @@ import "./chunk-BLEGIR35.js";
|
|
|
15
15
|
import fs from "fs";
|
|
16
16
|
var STATUS_LINE_RE = new RegExp(`^\\[[^\\]]*\\] ${STATUS_LINE_PREFIX} (.*)$`);
|
|
17
17
|
var HEARTBEAT_FIELDS_RE = /(^|\s)status=heartbeat(\s|$)/;
|
|
18
|
+
var ROTATED_FIELDS_RE = /(^|\s)status=rotated(\s|$)/;
|
|
19
|
+
var ROTATED_DROPPED_RE = /(^|\s)dropped=(\d+)(\s|$)/;
|
|
20
|
+
var MESSAGE_CURSOR_RE = /(^|\s)cursor=(\S+)(\s|$)/;
|
|
18
21
|
var HEARTBEAT_STALE_FLOOR_MS = envInt("KOJEE_TAIL_STALE_MS", 12e4);
|
|
19
22
|
var STALE_CHECK_INTERVAL_MS = envInt("KOJEE_TAIL_CHECK_MS", 5e3);
|
|
20
23
|
function envInt(name, fallback) {
|
|
@@ -38,14 +41,18 @@ async function runTail(messagesPath) {
|
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
};
|
|
44
|
+
let lastCursor = null;
|
|
41
45
|
function onMessageLine(line) {
|
|
42
46
|
lastLifeAt = Date.now();
|
|
43
47
|
if (staleAnnounced) {
|
|
44
48
|
staleAnnounced = false;
|
|
45
49
|
process.stdout.write("[kojee] stream recovered \u2014 events flowing again.\n");
|
|
46
50
|
}
|
|
51
|
+
const cm = MESSAGE_CURSOR_RE.exec(line);
|
|
52
|
+
if (cm && cm[2]) lastCursor = cm[2];
|
|
47
53
|
process.stdout.write(line + "\n");
|
|
48
54
|
}
|
|
55
|
+
let statusSeeded = false;
|
|
49
56
|
function onStatusLine(line) {
|
|
50
57
|
const now = Date.now();
|
|
51
58
|
const m = STATUS_LINE_RE.exec(line);
|
|
@@ -58,12 +65,26 @@ async function runTail(messagesPath) {
|
|
|
58
65
|
staleAnnounced = false;
|
|
59
66
|
process.stdout.write("[kojee] stream recovered \u2014 heartbeats resumed.\n");
|
|
60
67
|
}
|
|
68
|
+
if (statusSeeded && ROTATED_FIELDS_RE.test(fields)) {
|
|
69
|
+
const dm = ROTATED_DROPPED_RE.exec(fields);
|
|
70
|
+
const dropped = dm ? dm[2] : "?";
|
|
71
|
+
const since = lastCursor ? `since=${lastCursor}` : "since=<your last cursor>";
|
|
72
|
+
process.stdout.write(
|
|
73
|
+
`[kojee] WARNING: event log rotated (dropped=${dropped}) \u2014 older events were truncated and may have been missed; run tandem_messages(${since}) to backfill.
|
|
74
|
+
`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
61
77
|
}
|
|
62
78
|
const followers = [
|
|
63
79
|
makeFollower(messagesPath, onMessageLine, touchHeartbeat),
|
|
64
80
|
makeFollower(statusPath, onStatusLine, touchHeartbeat)
|
|
65
81
|
];
|
|
66
|
-
void Promise.all(
|
|
82
|
+
void Promise.all([
|
|
83
|
+
followers[0].start(),
|
|
84
|
+
followers[1].start().then(() => {
|
|
85
|
+
statusSeeded = true;
|
|
86
|
+
})
|
|
87
|
+
]);
|
|
67
88
|
const staleTimer = setInterval(() => {
|
|
68
89
|
if (staleAnnounced) return;
|
|
69
90
|
const now = Date.now();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kojee-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"jose": "^5.0.0",
|
|
34
34
|
"ps-list": "^8.1.1",
|
|
35
35
|
"ulidx": "^2.3.0",
|
|
36
|
+
"undici": "^8.5.0",
|
|
36
37
|
"zod": "^3.22.0"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|