kojee-mcp 0.5.15 → 0.5.16

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.
@@ -120,22 +120,10 @@ var GatewayClient = class {
120
120
  return result ?? { content: [{ type: "text", text: "No result" }] };
121
121
  }
122
122
  async sendHttpRequest(rpcRequest, signal) {
123
- const proof = await createDPoPProof(
124
- this.privateKey,
125
- this.kid,
126
- "POST",
127
- this.endpoint,
128
- this.currentNonce,
129
- this.token
130
- );
123
+ const headers = await this.buildAuthHeaders("POST", this.endpoint);
131
124
  return fetch(this.endpoint, {
132
125
  method: "POST",
133
- headers: {
134
- "Content-Type": "application/json",
135
- Authorization: `DPoP ${this.token}`,
136
- DPoP: proof,
137
- "Mcp-Session-Id": getSessionId()
138
- },
126
+ headers,
139
127
  body: JSON.stringify(rpcRequest),
140
128
  // ROUND-3 MAJOR A: the caller's AbortSignal rides HERE (a real fetch
141
129
  // option), never inside the JSON-RPC body. `undefined` is a valid value
@@ -143,6 +131,49 @@ var GatewayClient = class {
143
131
  ...signal ? { signal } : {}
144
132
  });
145
133
  }
134
+ /**
135
+ * Build the DPoP-authed request headers shared by every backend call: a fresh
136
+ * DPoP proof bound to (method, url) + the current nonce + the access token, the
137
+ * `Authorization: DPoP <token>` header, the stable `Mcp-Session-Id`, and the
138
+ * JSON content type. Factored out of sendHttpRequest so the REST helper below
139
+ * reuses the EXACT same auth construction (no auth-path divergence).
140
+ */
141
+ async buildAuthHeaders(method, url) {
142
+ const proof = await createDPoPProof(
143
+ this.privateKey,
144
+ this.kid,
145
+ method,
146
+ url,
147
+ this.currentNonce,
148
+ this.token
149
+ );
150
+ return {
151
+ "Content-Type": "application/json",
152
+ Authorization: `DPoP ${this.token}`,
153
+ DPoP: proof,
154
+ "Mcp-Session-Id": getSessionId()
155
+ };
156
+ }
157
+ /**
158
+ * Send an authed REST request (NOT JSON-RPC) to `<brokerUrl><path>`, reusing
159
+ * the SAME DPoP-proof + Authorization + Mcp-Session-Id header construction as
160
+ * the JSON-RPC path (buildAuthHeaders). Returns the RAW `Response` — callers
161
+ * own status handling; there is deliberately NO error-translation here, so a
162
+ * best-effort caller (presence) can fire-and-forget and swallow non-2xx.
163
+ *
164
+ * `body` is JSON-serialized when present (omitted otherwise). `signal` rides
165
+ * the real fetch option (per the JSON-RPC path's ROUND-3 convention).
166
+ */
167
+ async sendRest(method, path, body, signal) {
168
+ const url = `${this.brokerUrl}${path}`;
169
+ const headers = await this.buildAuthHeaders(method, url);
170
+ return fetch(url, {
171
+ method,
172
+ headers,
173
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {},
174
+ ...signal ? { signal } : {}
175
+ });
176
+ }
146
177
  trackNonce(response) {
147
178
  const nonce = response.headers.get("DPoP-Nonce");
148
179
  if (nonce) {
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  GatewayClient,
6
6
  applyStableSessionId
7
- } from "./chunk-IMOEZ4NJ.js";
7
+ } from "./chunk-34IRTWP6.js";
8
8
  import {
9
9
  AuthModule
10
10
  } from "./chunk-JXMVZEQ7.js";
@@ -312,7 +312,7 @@ async function startProxy(config) {
312
312
  }
313
313
  console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
314
314
  let server;
315
- const { selectDelivery } = await import("./registry-TGALQP6M.js");
315
+ const { selectDelivery } = await import("./registry-HPCRIWRF.js");
316
316
  const delivery = selectDelivery(adapter.runtime, {
317
317
  supportsChannels: adapter.supportsChannels
318
318
  });
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-3H3TL34J.js";
7
+ } from "./chunk-IRD26KZG.js";
8
8
  import "./chunk-XXFVWP6H.js";
9
9
  import {
10
10
  pairedConfigPath
11
11
  } from "./chunk-YH27B6SW.js";
12
- import "./chunk-IMOEZ4NJ.js";
12
+ import "./chunk-34IRTWP6.js";
13
13
  import "./chunk-JXMVZEQ7.js";
14
14
  import "./chunk-NR4Y54OL.js";
15
15
  import {
@@ -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-RH7D4JDP.js");
82
+ const { runSendCli } = await import("./send-cli-T6RPZZQ4.js");
83
83
  const { exitCode, envelope } = await runSendCli({
84
84
  tandemId,
85
85
  body: opts.body,
@@ -90,6 +90,25 @@ declare class GatewayClient {
90
90
  sendRpc(method: string, params?: Record<string, unknown>, signal?: AbortSignal): Promise<ToolCallResult>;
91
91
  private executeWithRetries;
92
92
  private sendHttpRequest;
93
+ /**
94
+ * Build the DPoP-authed request headers shared by every backend call: a fresh
95
+ * DPoP proof bound to (method, url) + the current nonce + the access token, the
96
+ * `Authorization: DPoP <token>` header, the stable `Mcp-Session-Id`, and the
97
+ * JSON content type. Factored out of sendHttpRequest so the REST helper below
98
+ * reuses the EXACT same auth construction (no auth-path divergence).
99
+ */
100
+ private buildAuthHeaders;
101
+ /**
102
+ * Send an authed REST request (NOT JSON-RPC) to `<brokerUrl><path>`, reusing
103
+ * the SAME DPoP-proof + Authorization + Mcp-Session-Id header construction as
104
+ * the JSON-RPC path (buildAuthHeaders). Returns the RAW `Response` — callers
105
+ * own status handling; there is deliberately NO error-translation here, so a
106
+ * best-effort caller (presence) can fire-and-forget and swallow non-2xx.
107
+ *
108
+ * `body` is JSON-serialized when present (omitted otherwise). `signal` rides
109
+ * the real fetch option (per the JSON-RPC path's ROUND-3 convention).
110
+ */
111
+ sendRest(method: string, path: string, body?: unknown, signal?: AbortSignal): Promise<Response>;
93
112
  private trackNonce;
94
113
  private tryParseErrorBody;
95
114
  }
@@ -45,6 +45,7 @@ async function handleRequest(req, res, opts) {
45
45
  if (req.method === "GET" && url.pathname === "/poll") {
46
46
  const type = url.searchParams.get("type") ?? "";
47
47
  const timeoutMs = Number.parseInt(url.searchParams.get("timeout_ms") ?? "0", 10);
48
+ fireLiveliness(opts.liveliness, type);
48
49
  if (type === "user-prompt-submit") {
49
50
  return respondWithEvents(res, opts);
50
51
  }
@@ -131,6 +132,17 @@ function readBody(req, maxBytes) {
131
132
  req.on("error", reject);
132
133
  });
133
134
  }
135
+ function fireLiveliness(liveliness, type) {
136
+ if (!liveliness) return;
137
+ try {
138
+ if (type === "user-prompt-submit") {
139
+ liveliness.onTurnStart();
140
+ } else if (type === "stop") {
141
+ liveliness.onTurnEnd();
142
+ }
143
+ } catch {
144
+ }
145
+ }
134
146
  function respondWithEvents(res, opts) {
135
147
  const entries = opts.queue.takeForHook();
136
148
  const events = entries.map((entry) => opts.adapter.formatTandemEvent(entry.event));
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { G as GatewayClient, P as ProxyConfig } from './gateway-client-C6yx1mfM.js';
1
+ import { G as GatewayClient, P as ProxyConfig } from './gateway-client-CbM2OC_w.js';
2
2
  import 'jose';
3
3
 
4
4
  /**
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  listTandemIds,
3
3
  startProxy
4
- } from "./chunk-3H3TL34J.js";
4
+ } from "./chunk-IRD26KZG.js";
5
5
  import "./chunk-XXFVWP6H.js";
6
- import "./chunk-IMOEZ4NJ.js";
6
+ import "./chunk-34IRTWP6.js";
7
7
  import "./chunk-JXMVZEQ7.js";
8
8
  import "./chunk-NR4Y54OL.js";
9
9
  import "./chunk-CH32ELFX.js";
package/dist/lib.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { KeyLike, JWK } from 'jose';
2
- import { L as LoadedKeyPair, K as KeystoreData, G as GatewayClient, M as McpToolDefinition, T as ToolCallResult, P as ProxyConfig } from './gateway-client-C6yx1mfM.js';
2
+ import { L as LoadedKeyPair, K as KeystoreData, G as GatewayClient, M as McpToolDefinition, T as ToolCallResult, P as ProxyConfig } from './gateway-client-CbM2OC_w.js';
3
3
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
4
 
5
5
  /**
package/dist/lib.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  GatewayClient,
15
15
  applyStableSessionId
16
- } from "./chunk-IMOEZ4NJ.js";
16
+ } from "./chunk-34IRTWP6.js";
17
17
  import {
18
18
  AuthModule
19
19
  } from "./chunk-JXMVZEQ7.js";
@@ -43,6 +43,40 @@ function formatChannelNotification(event) {
43
43
  return claudeCodeAdapter.formatTandemEvent(event);
44
44
  }
45
45
 
46
+ // src/delivery/liveliness.ts
47
+ function createPresenceProvider({
48
+ post,
49
+ debounceMs = 2e3
50
+ }) {
51
+ let idleTimer = null;
52
+ const clearIdleTimer = () => {
53
+ if (idleTimer !== null) {
54
+ clearTimeout(idleTimer);
55
+ idleTimer = null;
56
+ }
57
+ };
58
+ const emit = (state) => {
59
+ void post(state).catch(() => {
60
+ });
61
+ };
62
+ return {
63
+ onTurnStart() {
64
+ clearIdleTimer();
65
+ emit("working");
66
+ },
67
+ onTurnEnd() {
68
+ clearIdleTimer();
69
+ idleTimer = setTimeout(() => {
70
+ idleTimer = null;
71
+ emit("idle");
72
+ }, debounceMs);
73
+ },
74
+ stop() {
75
+ clearIdleTimer();
76
+ }
77
+ };
78
+ }
79
+
46
80
  // src/delivery/claude-code.ts
47
81
  function createClaudeCodeDelivery() {
48
82
  let eventLog = null;
@@ -52,11 +86,12 @@ function createClaudeCodeDelivery() {
52
86
  let hookServer = null;
53
87
  let cleanupDiscoveryFile = null;
54
88
  let streamHandle = null;
89
+ let liveliness = null;
55
90
  return {
56
91
  name: "claude-code",
57
92
  async start(ctx) {
58
93
  const { EventQueue } = await import("./event-queue-5YVJFR3E.js");
59
- const { startHookServer } = await import("./hook-server-T2Z444OV.js");
94
+ const { startHookServer } = await import("./hook-server-XK2NHLJV.js");
60
95
  const {
61
96
  writeDiscoveryByKey,
62
97
  cleanupDiscoveryByKey,
@@ -117,6 +152,9 @@ function createClaudeCodeDelivery() {
117
152
  err.message
118
153
  );
119
154
  }
155
+ liveliness = createPresenceProvider({
156
+ post: (state) => ctx.gateway.sendRest("POST", "/api/v2/presence", { state })
157
+ });
120
158
  queue = new EventQueue();
121
159
  const localQueue = queue;
122
160
  let liveStream = null;
@@ -124,6 +162,7 @@ function createClaudeCodeDelivery() {
124
162
  port: 0,
125
163
  queue: localQueue,
126
164
  adapter: ctx.adapter,
165
+ liveliness,
127
166
  ...controlToken !== null ? { controlToken, send: { gateway: ctx.gateway, authToken: controlToken } } : {},
128
167
  getStreamState: () => liveStream ? liveStream.getState() : {
129
168
  connected: false,
@@ -152,11 +191,15 @@ function createClaudeCodeDelivery() {
152
191
  cleanupDiscoveryFile = () => cleanupDiscoveryByKey(discoveryKey);
153
192
  const localCleanupDiscovery = cleanupDiscoveryFile;
154
193
  const localHookServer = hookServer;
194
+ const localLiveliness = liveliness;
155
195
  const exitCleanup = () => {
156
196
  localCleanupDiscovery();
157
197
  eventLog?.cleanup();
158
198
  };
159
199
  const teardown = [
200
+ () => {
201
+ localLiveliness.stop();
202
+ },
160
203
  () => {
161
204
  void webhookSink?.stop();
162
205
  },
@@ -207,6 +250,7 @@ function createClaudeCodeDelivery() {
207
250
  });
208
251
  },
209
252
  async stop() {
253
+ liveliness?.stop();
210
254
  await webhookSink?.stop();
211
255
  cleanupDiscoveryFile?.();
212
256
  eventLog?.cleanup();
@@ -10,7 +10,7 @@ import {
10
10
  import {
11
11
  GatewayClient,
12
12
  applyStableSessionId
13
- } from "./chunk-IMOEZ4NJ.js";
13
+ } from "./chunk-34IRTWP6.js";
14
14
  import "./chunk-NR4Y54OL.js";
15
15
  import {
16
16
  loadKeystore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kojee-mcp",
3
- "version": "0.5.15",
3
+ "version": "0.5.16",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {