@kodama-run/sdk 0.3.0 → 0.4.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/bin/cli.js +3061 -181
- package/bin/mcp.js +3037 -167
- package/package.json +3 -3
- package/src/adapters/mcp.ts +13 -23
- package/src/room.ts +9 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kodama-run/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts"
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"typescript": "*"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
|
-
"build": "
|
|
30
|
+
"build": "bun build src/adapters/mcp.ts --target=node --outfile=bin/mcp.js && bun build src/adapters/cli.ts --target=node --outfile=bin/cli.js && chmod +x bin/mcp.js bin/cli.js",
|
|
31
31
|
"test": "bun test",
|
|
32
32
|
"lint": "tsc --noEmit",
|
|
33
|
-
"clean": "rm -rf dist"
|
|
33
|
+
"clean": "rm -rf bin dist"
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/adapters/mcp.ts
CHANGED
|
@@ -167,7 +167,7 @@ const server = new McpServer(
|
|
|
167
167
|
"Kodama is a chatroom where AI agents have conversations on behalf of their owners.",
|
|
168
168
|
"",
|
|
169
169
|
"WORKFLOW:",
|
|
170
|
-
"1.
|
|
170
|
+
"1. kodama_create_and_join_room — creates a room AND auto-joins. Display the COMPLETE output to the user verbatim — ALL links (invite, owner view, spectate) and room code. Do NOT summarize or rephrase. Then IMMEDIATELY call kodama_listen to start waiting for the other agent.",
|
|
171
171
|
"2. kodama_listen — wait for messages. If status is 'waiting', call kodama_listen again. If status is 'your_turn', respond with kodama_say.",
|
|
172
172
|
"3. After kodama_say: immediately call kodama_listen again.",
|
|
173
173
|
"4. REPEAT steps 2-3. NEVER stop this loop unless the response says action=CONVERSATION_OVER. When you get CONVERSATION_OVER, call kodama_leave to disconnect, then summarize the outcome for the user.",
|
|
@@ -179,11 +179,11 @@ const server = new McpServer(
|
|
|
179
179
|
);
|
|
180
180
|
|
|
181
181
|
// ---------------------------------------------------------------------------
|
|
182
|
-
//
|
|
182
|
+
// kodama_create_and_join_room
|
|
183
183
|
// ---------------------------------------------------------------------------
|
|
184
184
|
|
|
185
185
|
server.tool(
|
|
186
|
-
"
|
|
186
|
+
"kodama_create_and_join_room",
|
|
187
187
|
"Create a Kodama chatroom and automatically join it. IMPORTANT: Display the returned output VERBATIM to the user — do not rephrase or summarize. Then immediately call kodama_listen to start waiting for the other agent. Do NOT wait for the user to tell you to proceed.",
|
|
188
188
|
{
|
|
189
189
|
topic: z.string().optional().describe("What this conversation is about, e.g. 'Dinner planning with Brandon'"),
|
|
@@ -238,22 +238,14 @@ server.tool(
|
|
|
238
238
|
await room.join();
|
|
239
239
|
} catch (err) {
|
|
240
240
|
turnHandlerInstalled = false;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (inviteRes.ok) {
|
|
250
|
-
const inviteData = (await inviteRes.json()) as { inviteToken: string };
|
|
251
|
-
newToken = inviteData.inviteToken;
|
|
252
|
-
}
|
|
253
|
-
} catch {}
|
|
254
|
-
|
|
255
|
-
savedInviteToken = newToken;
|
|
256
|
-
|
|
241
|
+
savedInviteToken = null;
|
|
242
|
+
|
|
243
|
+
// Note: we used to call POST /api/rooms/:code/invite here to mint a
|
|
244
|
+
// fresh invite token for retry. That endpoint now requires an
|
|
245
|
+
// ownerToken (security fix: otherwise anyone with a public room code
|
|
246
|
+
// could generate valid invite tokens), and we don't have one yet
|
|
247
|
+
// because the join itself failed. Tell the user to create a new
|
|
248
|
+
// room instead — simpler and safer.
|
|
257
249
|
const msg = (err as Error).message;
|
|
258
250
|
return {
|
|
259
251
|
content: [{
|
|
@@ -267,9 +259,7 @@ server.tool(
|
|
|
267
259
|
`Share with your friend:`,
|
|
268
260
|
`${WEB_URL}/room/${data.code}#invite=${friendToken}`,
|
|
269
261
|
``,
|
|
270
|
-
|
|
271
|
-
? `Your invite token has been saved. Call kodama_join to retry.`
|
|
272
|
-
: `Could not regenerate invite token. Create a new room instead.`,
|
|
262
|
+
`Call kodama_create_and_join_room again to start fresh.`,
|
|
273
263
|
].join("\n"),
|
|
274
264
|
}],
|
|
275
265
|
isError: true,
|
|
@@ -328,7 +318,7 @@ server.tool(
|
|
|
328
318
|
|
|
329
319
|
if (!code || !token) {
|
|
330
320
|
return {
|
|
331
|
-
content: [{ type: "text" as const, text: "Missing room_code or invite_token. Create a room first with
|
|
321
|
+
content: [{ type: "text" as const, text: "Missing room_code or invite_token. Create a room first with kodama_create_and_join_room, or provide both parameters." }],
|
|
332
322
|
isError: true,
|
|
333
323
|
};
|
|
334
324
|
}
|
package/src/room.ts
CHANGED
|
@@ -9,6 +9,12 @@ import { createAgentCard } from "@kodama-run/shared";
|
|
|
9
9
|
import { KodamaCrypto } from "./crypto.ts";
|
|
10
10
|
import { PermissionChecker } from "./permissions.ts";
|
|
11
11
|
|
|
12
|
+
// Use ws package for Node <22 compatibility, native WebSocket otherwise
|
|
13
|
+
// @ts-ignore - dynamic require for Node compat
|
|
14
|
+
const WS: typeof WebSocket = typeof globalThis.WebSocket !== "undefined"
|
|
15
|
+
? globalThis.WebSocket
|
|
16
|
+
: (() => { try { return require("ws"); } catch { return null; } })();
|
|
17
|
+
|
|
12
18
|
// ---------------------------------------------------------------------------
|
|
13
19
|
// Types
|
|
14
20
|
// ---------------------------------------------------------------------------
|
|
@@ -139,7 +145,7 @@ export class Kodama {
|
|
|
139
145
|
*/
|
|
140
146
|
leave(): void {
|
|
141
147
|
this.closed = true;
|
|
142
|
-
if (this.ws && this.ws.readyState ===
|
|
148
|
+
if (this.ws && this.ws.readyState === WS.OPEN) {
|
|
143
149
|
this.ws.send(JSON.stringify({ action: "leave" }));
|
|
144
150
|
this.ws.close();
|
|
145
151
|
}
|
|
@@ -172,7 +178,7 @@ export class Kodama {
|
|
|
172
178
|
// -----------------------------------------------------------------------
|
|
173
179
|
|
|
174
180
|
private connect(): void {
|
|
175
|
-
const ws = new
|
|
181
|
+
const ws = new WS(this.relayUrl) as unknown as WebSocket;
|
|
176
182
|
this.ws = ws;
|
|
177
183
|
|
|
178
184
|
ws.addEventListener("open", () => {
|
|
@@ -343,7 +349,7 @@ export class Kodama {
|
|
|
343
349
|
// Filter response through permissions
|
|
344
350
|
const filtered = this.permissions.filterMessage(content);
|
|
345
351
|
|
|
346
|
-
if (this.ws && this.ws.readyState ===
|
|
352
|
+
if (this.ws && this.ws.readyState === WS.OPEN) {
|
|
347
353
|
this.ws.send(
|
|
348
354
|
JSON.stringify({
|
|
349
355
|
action: "message",
|