kojee-mcp 0.5.19 → 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-PSVVRGJC.js → chunk-BYV7CWNY.js} +4 -4
- package/dist/{chunk-35XBRG3V.js → chunk-KFDZWNQO.js} +1 -1
- package/dist/{chunk-U4MNICQI.js → chunk-KH5YEJIM.js} +5 -2
- 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-U3MTB4GY.js → doctor-WVX3WCYX.js} +2 -2
- package/dist/{doctor-codex-PA3WO6LR.js → doctor-codex-Z5M4SFBG.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.js +3 -3
- package/dist/{registry-CGLE5X54.js → registry-YLOFZDI2.js} +10 -10
- package/dist/{send-cli-IPADRDD2.js → send-cli-IYJF32PE.js} +3 -3
- 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-QZTFH5WV.js → stop-hook-TCPJFGFF.js} +1 -1
- package/dist/{tail-stream-CEJZGSIR.js → tail-stream-4IMXI44S.js} +23 -2
- 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";
|
|
@@ -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
|
});
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
translateHttpError,
|
|
9
9
|
translateJsonRpcError,
|
|
10
10
|
translateNetworkError
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-PPTKGWFF.js";
|
|
12
12
|
|
|
13
13
|
// src/gateway-client.ts
|
|
14
14
|
import crypto from "crypto";
|
|
@@ -104,8 +104,11 @@ var GatewayClient = class {
|
|
|
104
104
|
const body = await this.tryParseErrorBody(response);
|
|
105
105
|
const translated = translateHttpError(response.status, body?.error);
|
|
106
106
|
if (translated) return translated;
|
|
107
|
+
const detail = body?.error_description ?? body?.detail ?? body?.error ?? "<no detail>";
|
|
107
108
|
return {
|
|
108
|
-
content: [
|
|
109
|
+
content: [
|
|
110
|
+
{ type: "text", text: `Gateway error ${response.status}: ${detail}` }
|
|
111
|
+
],
|
|
109
112
|
isError: true
|
|
110
113
|
};
|
|
111
114
|
}
|
|
@@ -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,
|
|
@@ -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.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,7 +29,7 @@ import {
|
|
|
29
29
|
saveKeystore
|
|
30
30
|
} from "./chunk-CH32ELFX.js";
|
|
31
31
|
import "./chunk-BLEGIR35.js";
|
|
32
|
-
import "./chunk-
|
|
32
|
+
import "./chunk-PPTKGWFF.js";
|
|
33
33
|
export {
|
|
34
34
|
AuthModule,
|
|
35
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,7 +91,7 @@ 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,
|
|
@@ -101,8 +101,8 @@ function createClaudeCodeDelivery() {
|
|
|
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();
|
|
@@ -278,8 +278,8 @@ function createWebhookDelivery(name) {
|
|
|
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,20 +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-
|
|
19
|
+
import "./chunk-PPTKGWFF.js";
|
|
20
20
|
|
|
21
21
|
// src/tandem/send-cli.ts
|
|
22
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
|
};
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
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": {
|