figma-relai 0.2.7 → 0.2.8
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-VNOKL7LL.js → chunk-RDSEEOKP.js} +8 -3
- package/dist/chunk-RDSEEOKP.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{manifest-CC3RJVMQ.js → manifest-HMHQAF6T.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-VNOKL7LL.js.map +0 -1
- /package/dist/{manifest-CC3RJVMQ.js.map → manifest-HMHQAF6T.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
registerEventsTool,
|
|
8
8
|
registerPrompts,
|
|
9
9
|
registerRoomTool
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-RDSEEOKP.js";
|
|
11
11
|
import {
|
|
12
12
|
loadState,
|
|
13
13
|
saveState
|
|
@@ -427,7 +427,7 @@ function getSessionLog() {
|
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
// src/index.ts
|
|
430
|
-
var VERSION = "0.2.
|
|
430
|
+
var VERSION = "0.2.8";
|
|
431
431
|
var args = process.argv.slice(2);
|
|
432
432
|
var serverArg = args.find((arg) => arg.startsWith("--server="));
|
|
433
433
|
var serverUrl = serverArg ? serverArg.split("=")[1] : "localhost";
|
|
@@ -440,7 +440,7 @@ if (args.includes("--list-tools")) {
|
|
|
440
440
|
}
|
|
441
441
|
var subcommand = args.find((a) => !a.startsWith("--"));
|
|
442
442
|
if (subcommand === "manifest" || subcommand === "docs" || subcommand === "doctor") {
|
|
443
|
-
const { buildManifest } = await import("./manifest-
|
|
443
|
+
const { buildManifest } = await import("./manifest-HMHQAF6T.js");
|
|
444
444
|
if (subcommand === "doctor") {
|
|
445
445
|
const { runDoctor, renderDoctor } = await import("./doctor-ZN2PKAPH.js");
|
|
446
446
|
const results = await runDoctor();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/connection.ts","../src/logger.ts","../src/request-tracker.ts","../src/embedded-relay.ts","../src/session-log.ts"],"sourcesContent":["import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createServer } from \"./server.js\";\nimport { FigmaConnection } from \"./connection.js\";\nimport { logger } from \"./logger.js\";\nimport {\n registerAllTools,\n registerRoomTool,\n registerEventsTool,\n listToolCatalog,\n} from \"./tools/index.js\";\nimport { startEmbeddedRelay, type EmbeddedRelay } from \"./embedded-relay.js\";\nimport { loadState, saveState } from \"./state.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { recordCommand, getSessionLog } from \"./session-log.js\";\n\nconst VERSION = \"0.2.7\";\n\n// Parse CLI arguments\nconst args = process.argv.slice(2);\nconst serverArg = args.find((arg) => arg.startsWith(\"--server=\"));\nconst serverUrl = serverArg ? serverArg.split(\"=\")[1] : \"localhost\";\n\nconst portArg = args.find((arg) => arg.startsWith(\"--port=\"));\nconst port = portArg ? parseInt(portArg.split(\"=\")[1]) : 9055;\n\nconst roomArg = args.find((arg) => arg.startsWith(\"--room=\"));\n\n// Build-time inventory for the plugin UI tool list; exits without stdio setup\nif (args.includes(\"--list-tools\")) {\n console.log(JSON.stringify(listToolCatalog(), null, 2));\n process.exit(0);\n}\n\n// CLI subcommands (Astryx-style: the contract is executable, not prose).\n// `manifest` prints the machine-readable contract, `docs` renders it for\n// humans, `doctor` triages the local environment. All exit before stdio.\nconst subcommand = args.find((a) => !a.startsWith(\"--\"));\nif (subcommand === \"manifest\" || subcommand === \"docs\" || subcommand === \"doctor\") {\n const { buildManifest } = await import(\"./cli/manifest.js\");\n if (subcommand === \"doctor\") {\n const { runDoctor, renderDoctor } = await import(\"./cli/doctor.js\");\n const results = await runDoctor();\n console.log(args.includes(\"--json\") ? JSON.stringify(results, null, 2) : renderDoctor(results));\n process.exit(results.some((r) => r.status === \"warn\") ? 1 : 0);\n }\n const manifest = await buildManifest(VERSION);\n if (subcommand === \"manifest\") {\n console.log(JSON.stringify(manifest, null, 2));\n } else {\n const { renderToolDoc, renderToolIndex } = await import(\"./cli/docs.js\");\n const toolName = args[args.indexOf(\"docs\") + 1];\n console.log(\n toolName && !toolName.startsWith(\"--\")\n ? renderToolDoc(manifest, toolName)\n : renderToolIndex(manifest)\n );\n }\n process.exit(0);\n}\n\nasync function main() {\n // Create MCP server\n const server = createServer();\n\n // Host the relay in this process unless another instance already does\n // (bind-or-connect: first MCP server binds 9055, later ones connect to it).\n // Only for local relays — a remote --server means someone runs it there.\n let relay: EmbeddedRelay | null = null;\n if (serverUrl === \"localhost\") {\n relay = await startEmbeddedRelay(port, VERSION);\n if (!relay) {\n logger.info(`Port ${port} in use — connecting to the existing relay`);\n }\n }\n\n const initialRoom =\n roomArg?.split(\"=\")[1] ?? process.env.FIGMA_RELAI_ROOM ?? loadState().room ?? null;\n\n // Create WebSocket connection to relay\n const connection = new FigmaConnection(serverUrl, port, {\n initialRoom,\n onRoomChanged: (room) => saveState({ room }),\n beforeReconnect: async () => {\n // The hosting process may have exited; take over if the port is free\n if (serverUrl === \"localhost\" && !relay) {\n relay = await startEmbeddedRelay(port, VERSION);\n if (relay) logger.info(\"Took over relay hosting\");\n }\n },\n });\n\n // Register the join_room tool\n registerRoomTool(server, (room) => connection.joinRoom(room));\n\n // Register designer-activity polling + the AI's own audit trail\n registerEventsTool(server, () => connection.consumeEvents(), getSessionLog);\n\n // Expose skill documents as MCP prompts (inlined at build time)\n registerPrompts(server);\n\n // Register all domain tools; every plugin command lands in the session log\n registerAllTools(server, async (command, params, timeoutMs) => {\n const t0 = Date.now();\n const nodeId =\n typeof (params as { nodeId?: unknown })?.nodeId === \"string\"\n ? ((params as { nodeId: string }).nodeId)\n : undefined;\n try {\n const result = await connection.sendCommand(command, params, timeoutMs);\n recordCommand({ ts: t0, command, nodeId, ok: true, ms: Date.now() - t0 });\n return result;\n } catch (error) {\n recordCommand({\n ts: t0,\n command,\n nodeId,\n ok: false,\n ms: Date.now() - t0,\n error: error instanceof Error ? error.message.slice(0, 200) : String(error),\n });\n throw error;\n }\n });\n\n // Connect to relay (auto-reconnects on failure)\n try {\n await connection.connect();\n logger.info(\"Connected to relay successfully\");\n } catch (error) {\n logger.warn(\n `Could not connect initially: ${error instanceof Error ? error.message : String(error)}`\n );\n logger.warn(\"Will attempt to connect when the first command is sent\");\n }\n\n // Start MCP server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info(\"Relai MCP server running on stdio\");\n}\n\nmain().catch((error) => {\n logger.error(\n `Fatal error: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n});\n","import WebSocket from \"ws\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { logger } from \"./logger.js\";\nimport { RequestTracker } from \"./request-tracker.js\";\nimport type { FigmaCommand } from \"@figma-relai/shared\";\nimport type { CommandProgressUpdate, RoomSummary } from \"@figma-relai/shared\";\n\nexport interface FigmaConnectionOptions {\n // Room restored from persisted state; used to auto-pair on the first command\n initialRoom?: string | null;\n // Called whenever a room is joined so the caller can persist it\n onRoomChanged?: (room: string) => void;\n // Called before each reconnect attempt (e.g. to take over relay hosting)\n beforeReconnect?: () => Promise<void>;\n}\n\n// Error payload sent by the plugin: a plain string, or an object with context\ninterface PluginErrorPayload {\n message?: string;\n command?: string;\n nodeId?: string;\n nodeType?: string;\n}\n\n// Flatten a plugin error (string or structured object) into one readable line\nexport function formatFigmaError(e: unknown): string {\n if (typeof e === \"string\") return e;\n const { message, command, nodeId, nodeType } = (e ?? {}) as PluginErrorPayload;\n const prefix = command ? `[${command}] ` : \"\";\n const suffix = nodeId\n ? ` (node ${nodeId}${nodeType ? `, type ${nodeType}` : \"\"})`\n : \"\";\n return `${prefix}${message ?? JSON.stringify(e)}${suffix}`;\n}\n\n// Route a response message to its pending request. Error responses have no\n// `result` field, so the error check must come first and resolution must key\n// on field presence — falsy results (0, \"\", false) are valid.\nexport function routeResponse(\n response: { id?: string; result?: unknown; error?: unknown } | undefined,\n tracker: RequestTracker\n): void {\n if (!response?.id || !tracker.has(response.id)) return;\n if (response.error !== undefined) {\n tracker.reject(response.id, new Error(formatFigmaError(response.error)));\n } else if (\"result\" in response) {\n tracker.resolve(response.id, response.result);\n }\n}\n\n// Manages WebSocket connection to the relay server\nexport class FigmaConnection {\n private ws: WebSocket | null = null;\n private currentRoom: string | null = null;\n private tracker = new RequestTracker();\n private wsUrl: string;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private shouldReconnect = false;\n private connectPromise: Promise<void> | null = null;\n private commandHandler: ((command: string, params: unknown) => void) | null = null;\n private options: FigmaConnectionOptions;\n private relayVerified = false;\n // Presence recorded per room: join-time broadcasts can arrive before\n // currentRoom is updated, so keying by room avoids the ordering race\n private presenceByRoom = new Map<string, boolean>();\n // Designer activity piggybacked on plugin responses (selection changes etc.)\n private eventQueue: unknown[] = [];\n\n constructor(\n serverUrl: string = \"localhost\",\n port: number = 9055,\n options: FigmaConnectionOptions = {}\n ) {\n const protocol = serverUrl === \"localhost\" ? \"ws\" : \"wss\";\n this.wsUrl =\n serverUrl === \"localhost\"\n ? `${protocol}://${serverUrl}:${port}`\n : `${protocol}://${serverUrl}`;\n this.options = options;\n }\n\n // Connect to WebSocket relay server\n connect(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n logger.info(\"Already connected to relay\");\n return Promise.resolve();\n }\n\n // If already connecting, return existing promise\n if (this.connectPromise) {\n return this.connectPromise;\n }\n\n logger.info(`Connecting to relay at ${this.wsUrl}...`);\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n const ws = new WebSocket(this.wsUrl);\n this.ws = ws;\n\n const cleanup = () => {\n this.connectPromise = null;\n };\n\n ws.on(\"open\", () => {\n logger.info(\"Connected to relay server\");\n this.shouldReconnect = true;\n cleanup();\n resolve();\n\n // Verify we reached a Relai relay (and not some other port 9055 app)\n this.relayVerified = false;\n try {\n ws.send(JSON.stringify({ type: \"hello\" }));\n } catch {\n // Send failure surfaces via close/error handlers\n }\n setTimeout(() => {\n if (!this.relayVerified && this.ws === ws) {\n logger.warn(\n \"Connected endpoint did not identify as a figma-relai relay — is another app using the port?\"\n );\n }\n }, 1500);\n\n // Rejoin the previous room after a relay restart / reconnect so the\n // AI doesn't land in a silent \"must join a room\" dead state\n if (this.currentRoom) {\n const room = this.currentRoom;\n this.sendCommand(\"join\", { room }).then(\n () => logger.info(`Rejoined room: ${room}`),\n (err) =>\n logger.warn(\n `Failed to rejoin room ${room}: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n }\n });\n\n ws.on(\"message\", (data: WebSocket.Data) => {\n try {\n const json = JSON.parse(data.toString());\n\n // Relay identity probe response\n if (json.type === \"hello\" && json.server === \"figma-relai\") {\n this.relayVerified = true;\n logger.debug(`Relay verified (version ${json.version})`);\n return;\n }\n\n // Peer presence per room (is the plugin there?)\n if (json.type === \"presence\") {\n if (typeof json.room === \"string\" && Array.isArray(json.peers)) {\n this.presenceByRoom.set(\n json.room,\n json.peers.some((p: { role?: string }) => p.role === \"plugin\")\n );\n }\n return;\n }\n\n // Room listing response (auto-pairing)\n if (json.type === \"list_rooms_result\" && json.id) {\n this.tracker.resolve(json.id, json.rooms ?? []);\n return;\n }\n\n // Handle incoming commands (from plugin UI broadcasts)\n if (json.type === \"broadcast\" && json.message?.command) {\n this.commandHandler?.(json.message.command, json.message.params);\n return;\n }\n\n // Handle progress updates\n if (json.type === \"progress_update\") {\n const progressData = json.message?.data as CommandProgressUpdate;\n const requestId = json.id || \"\";\n\n if (requestId && this.tracker.has(requestId)) {\n this.tracker.resetTimeout(requestId);\n logger.info(\n `Progress: ${progressData.commandType} ${progressData.progress}% - ${progressData.message}`\n );\n }\n return;\n }\n\n // Collect designer-activity events piggybacked on responses\n if (Array.isArray(json.message?.events)) {\n this.eventQueue.push(...json.message.events);\n if (this.eventQueue.length > 50) {\n this.eventQueue = this.eventQueue.slice(-50);\n }\n }\n\n // Handle regular responses\n routeResponse(json.message, this.tracker);\n } catch (error) {\n logger.error(\n `Error parsing message: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n });\n\n ws.on(\"error\", (error) => {\n logger.error(`WebSocket error: ${error.message}`);\n cleanup();\n reject(error);\n });\n\n ws.on(\"close\", () => {\n logger.info(\"Disconnected from relay\");\n this.ws = null;\n this.tracker.rejectAll(\"Connection closed\");\n cleanup();\n\n // Only auto-reconnect if not manually disconnected. beforeReconnect\n // lets the host process try to re-bind the relay port first (host\n // takeover when the previous hosting MCP server exited).\n if (this.shouldReconnect) {\n this.reconnectTimer = setTimeout(async () => {\n try {\n await this.options.beforeReconnect?.();\n } catch {\n // Takeover failure is fine; we may connect to another host\n }\n this.connect().catch(() => {});\n }, 2000);\n }\n });\n });\n\n return this.connectPromise;\n }\n\n // Disconnect and stop reconnection\n disconnect(): void {\n this.shouldReconnect = false;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n // Join a room for communication\n async joinRoom(roomName: string): Promise<void> {\n await this.sendCommand(\"join\", { room: roomName });\n this.currentRoom = roomName;\n this.options.onRoomChanged?.(roomName);\n logger.info(`Joined room: ${roomName}`);\n }\n\n // Ask the relay which rooms exist and who is in them\n listRooms(): Promise<RoomSummary[]> {\n return new Promise((resolve, reject) => {\n const id = uuidv4();\n this.tracker.add(id, resolve as (value: unknown) => void, reject, 5000);\n this.ws!.send(JSON.stringify({ type: \"list_rooms\", id }));\n });\n }\n\n // Auto-pair with the plugin so join_room is only needed for disambiguation:\n // prefer the persisted room if its plugin is present, otherwise join the\n // single plugin room, otherwise explain exactly what to do.\n private async ensureRoom(): Promise<void> {\n if (this.currentRoom) return;\n\n const rooms = await this.listRooms();\n const withPlugin = rooms.filter((r) => r.hasPlugin);\n const saved = this.options.initialRoom;\n\n if (saved && withPlugin.some((r) => r.room === saved)) {\n return this.joinRoom(saved);\n }\n if (withPlugin.length === 1) {\n return this.joinRoom(withPlugin[0].room);\n }\n if (withPlugin.length === 0) {\n throw new Error(\n \"No Figma plugin is connected. Open the Relai plugin in Figma (it connects automatically), then try again.\"\n );\n }\n throw new Error(\n `Multiple Figma plugins are connected: ${withPlugin\n .map((r) => (r.fileName ? `\"${r.fileName}\" (room ${r.room})` : `room ${r.room}`))\n .join(\", \")}. Call join_room with the room of the file you want to control.`\n );\n }\n\n // Send a command to Figma plugin via the relay\n async sendCommand(\n command: FigmaCommand,\n params: unknown = {},\n timeoutMs: number = 30000\n ): Promise<unknown> {\n // Wait for connection if not connected\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n try {\n await this.connect();\n } catch {\n // The relay host may have just exited (port-handoff race on rapid\n // server restarts): try to take over hosting, then dial once more.\n try {\n await this.options.beforeReconnect?.();\n } catch {\n // Takeover failure is fine; the retry below may reach another host\n }\n try {\n await this.connect();\n } catch {\n throw new Error(\n \"Not connected to the relay. The MCP server hosts it automatically — if this persists, another app may be occupying the port.\"\n );\n }\n }\n }\n\n if (command !== \"join\") {\n await this.ensureRoom();\n // Presence said the room has no plugin — fail fast, not a 30s timeout\n if (this.currentRoom && this.presenceByRoom.get(this.currentRoom) === false) {\n throw new Error(\n \"The Figma plugin is not open. Open the Relai plugin in Figma — it will reconnect to the same room automatically.\"\n );\n }\n }\n\n const result = await new Promise((resolve, reject) => {\n const id = uuidv4();\n const request = {\n id,\n type: command === \"join\" ? \"join\" : \"message\",\n room:\n command === \"join\"\n ? (params as { room: string }).room\n : this.currentRoom,\n ...(command === \"join\"\n ? { role: \"agent\", meta: { client: \"figma-relai-mcp\" } }\n : {}),\n message: {\n id,\n command,\n params: {\n ...(params as Record<string, unknown>),\n commandId: id,\n },\n },\n };\n\n this.tracker.add(id, resolve, reject, timeoutMs);\n\n logger.info(`Sending command: ${command}`);\n logger.debug(`Request: ${JSON.stringify(request)}`);\n this.ws!.send(JSON.stringify(request));\n });\n\n // Surface designer activity alongside object results so the AI sees it\n // without polling; get_events covers the remaining cases\n if (\n this.eventQueue.length > 0 &&\n result !== null &&\n typeof result === \"object\" &&\n !Array.isArray(result)\n ) {\n return { ...(result as Record<string, unknown>), designer_events: this.consumeEvents() };\n }\n return result;\n }\n\n // Drain buffered designer-activity events\n consumeEvents(): unknown[] {\n const drained = this.eventQueue;\n this.eventQueue = [];\n return drained;\n }\n\n // Set handler for incoming commands from other room members (e.g. plugin UI)\n setCommandHandler(handler: (command: string, params: unknown) => void): void {\n this.commandHandler = handler;\n }\n\n get isConnected(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN;\n }\n\n get room(): string | null {\n return this.currentRoom;\n }\n}\n","// Logger that writes to stderr to keep stdout clean for MCP protocol\n\nexport const logger = {\n info: (message: string) => process.stderr.write(`[INFO] ${message}\\n`),\n debug: (message: string) => process.stderr.write(`[DEBUG] ${message}\\n`),\n warn: (message: string) => process.stderr.write(`[WARN] ${message}\\n`),\n error: (message: string) => process.stderr.write(`[ERROR] ${message}\\n`),\n};\n","import { logger } from \"./logger.js\";\n\n// Pending request entry with resolve/reject callbacks and timeout tracking\nexport interface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n timeout: ReturnType<typeof setTimeout>;\n lastActivity: number;\n}\n\n// Manages pending command requests with timeout and progress update support\nexport class RequestTracker {\n private pending = new Map<string, PendingRequest>();\n\n // Register a new pending request with timeout\n add(\n id: string,\n resolve: (value: unknown) => void,\n reject: (reason: unknown) => void,\n timeoutMs: number = 30000\n ): void {\n const timeout = setTimeout(() => {\n if (this.pending.has(id)) {\n this.pending.delete(id);\n logger.error(`Request ${id} timed out after ${timeoutMs / 1000}s`);\n reject(new Error(\"Request to Figma timed out\"));\n }\n }, timeoutMs);\n\n this.pending.set(id, {\n resolve,\n reject,\n timeout,\n lastActivity: Date.now(),\n });\n }\n\n // Resolve a request with result\n resolve(id: string, result: unknown): boolean {\n const request = this.pending.get(id);\n if (!request) return false;\n\n clearTimeout(request.timeout);\n request.resolve(result);\n this.pending.delete(id);\n return true;\n }\n\n // Reject a request with error\n reject(id: string, error: Error): boolean {\n const request = this.pending.get(id);\n if (!request) return false;\n\n clearTimeout(request.timeout);\n request.reject(error);\n this.pending.delete(id);\n return true;\n }\n\n // Reset timeout on progress update to prevent timeout during long operations\n resetTimeout(id: string, extendedTimeoutMs: number = 60000): void {\n const request = this.pending.get(id);\n if (!request) return;\n\n request.lastActivity = Date.now();\n clearTimeout(request.timeout);\n request.timeout = setTimeout(() => {\n if (this.pending.has(id)) {\n this.pending.delete(id);\n logger.error(`Request ${id} timed out after extended inactivity`);\n request.reject(new Error(\"Request to Figma timed out\"));\n }\n }, extendedTimeoutMs);\n }\n\n // Check if a request is pending\n has(id: string): boolean {\n return this.pending.has(id);\n }\n\n // Reject all pending requests (e.g., on connection close)\n rejectAll(reason: string): void {\n for (const [id, request] of this.pending.entries()) {\n clearTimeout(request.timeout);\n request.reject(new Error(reason));\n }\n this.pending.clear();\n }\n\n get size(): number {\n return this.pending.size;\n }\n}\n","import { createServer } from \"node:http\";\nimport { WebSocketServer, type WebSocket } from \"ws\";\nimport { RelayCore } from \"@figma-relai/shared\";\nimport { logger } from \"./logger.js\";\n\nexport interface EmbeddedRelay {\n close(): void;\n}\n\n// Host the relay in-process on 127.0.0.1:port. Returns null if the port is\n// already taken — another MCP server instance (e.g. Cursor and Claude Code\n// open at once) is hosting, and this process should just connect as a client.\nexport function startEmbeddedRelay(\n port: number,\n version: string\n): Promise<EmbeddedRelay | null> {\n return new Promise((resolve) => {\n const httpServer = createServer();\n\n httpServer.once(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n resolve(null);\n } else {\n logger.warn(`Embedded relay failed to start: ${err.message}`);\n resolve(null);\n }\n });\n\n httpServer.listen(port, \"127.0.0.1\", () => {\n const core = new RelayCore<WebSocket>({\n version,\n log: (msg) => logger.debug(`[relay] ${msg}`),\n });\n const wss = new WebSocketServer({ server: httpServer });\n\n wss.on(\"connection\", (ws) => {\n core.handleOpen(ws);\n ws.on(\"message\", (data) => core.handleMessage(ws, data.toString()));\n ws.on(\"close\", () => core.handleClose(ws));\n ws.on(\"error\", () => {});\n });\n\n core.startStaleCleanup();\n logger.info(`Hosting embedded relay on 127.0.0.1:${port}`);\n\n resolve({\n close() {\n core.stop();\n wss.close();\n httpServer.close();\n },\n });\n });\n });\n}\n","// Ring buffer of every command this server sent to the plugin — the AI's own\n// audit trail, surfaced through get_events (scope: \"agent\"). Complements the\n// plugin's activity feed: the designer sees it live, the AI can review it.\n\nexport interface SessionLogEntry {\n ts: number;\n command: string;\n nodeId?: string;\n ok: boolean;\n ms: number;\n error?: string;\n}\n\nconst MAX_ENTRIES = 200;\nlet entries: SessionLogEntry[] = [];\n\nexport function recordCommand(entry: SessionLogEntry): void {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries = entries.slice(-MAX_ENTRIES);\n}\n\nexport function getSessionLog(): SessionLogEntry[] {\n return [...entries];\n}\n\nexport function clearSessionLog(): void {\n entries = [];\n}\n"],"mappings":"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","names":["serverUrl","port","createServer","port","createServer"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/connection.ts","../src/logger.ts","../src/request-tracker.ts","../src/embedded-relay.ts","../src/session-log.ts"],"sourcesContent":["import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createServer } from \"./server.js\";\nimport { FigmaConnection } from \"./connection.js\";\nimport { logger } from \"./logger.js\";\nimport {\n registerAllTools,\n registerRoomTool,\n registerEventsTool,\n listToolCatalog,\n} from \"./tools/index.js\";\nimport { startEmbeddedRelay, type EmbeddedRelay } from \"./embedded-relay.js\";\nimport { loadState, saveState } from \"./state.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { recordCommand, getSessionLog } from \"./session-log.js\";\n\nconst VERSION = \"0.2.8\";\n\n// Parse CLI arguments\nconst args = process.argv.slice(2);\nconst serverArg = args.find((arg) => arg.startsWith(\"--server=\"));\nconst serverUrl = serverArg ? serverArg.split(\"=\")[1] : \"localhost\";\n\nconst portArg = args.find((arg) => arg.startsWith(\"--port=\"));\nconst port = portArg ? parseInt(portArg.split(\"=\")[1]) : 9055;\n\nconst roomArg = args.find((arg) => arg.startsWith(\"--room=\"));\n\n// Build-time inventory for the plugin UI tool list; exits without stdio setup\nif (args.includes(\"--list-tools\")) {\n console.log(JSON.stringify(listToolCatalog(), null, 2));\n process.exit(0);\n}\n\n// CLI subcommands (Astryx-style: the contract is executable, not prose).\n// `manifest` prints the machine-readable contract, `docs` renders it for\n// humans, `doctor` triages the local environment. All exit before stdio.\nconst subcommand = args.find((a) => !a.startsWith(\"--\"));\nif (subcommand === \"manifest\" || subcommand === \"docs\" || subcommand === \"doctor\") {\n const { buildManifest } = await import(\"./cli/manifest.js\");\n if (subcommand === \"doctor\") {\n const { runDoctor, renderDoctor } = await import(\"./cli/doctor.js\");\n const results = await runDoctor();\n console.log(args.includes(\"--json\") ? JSON.stringify(results, null, 2) : renderDoctor(results));\n process.exit(results.some((r) => r.status === \"warn\") ? 1 : 0);\n }\n const manifest = await buildManifest(VERSION);\n if (subcommand === \"manifest\") {\n console.log(JSON.stringify(manifest, null, 2));\n } else {\n const { renderToolDoc, renderToolIndex } = await import(\"./cli/docs.js\");\n const toolName = args[args.indexOf(\"docs\") + 1];\n console.log(\n toolName && !toolName.startsWith(\"--\")\n ? renderToolDoc(manifest, toolName)\n : renderToolIndex(manifest)\n );\n }\n process.exit(0);\n}\n\nasync function main() {\n // Create MCP server\n const server = createServer();\n\n // Host the relay in this process unless another instance already does\n // (bind-or-connect: first MCP server binds 9055, later ones connect to it).\n // Only for local relays — a remote --server means someone runs it there.\n let relay: EmbeddedRelay | null = null;\n if (serverUrl === \"localhost\") {\n relay = await startEmbeddedRelay(port, VERSION);\n if (!relay) {\n logger.info(`Port ${port} in use — connecting to the existing relay`);\n }\n }\n\n const initialRoom =\n roomArg?.split(\"=\")[1] ?? process.env.FIGMA_RELAI_ROOM ?? loadState().room ?? null;\n\n // Create WebSocket connection to relay\n const connection = new FigmaConnection(serverUrl, port, {\n initialRoom,\n onRoomChanged: (room) => saveState({ room }),\n beforeReconnect: async () => {\n // The hosting process may have exited; take over if the port is free\n if (serverUrl === \"localhost\" && !relay) {\n relay = await startEmbeddedRelay(port, VERSION);\n if (relay) logger.info(\"Took over relay hosting\");\n }\n },\n });\n\n // Register the join_room tool\n registerRoomTool(server, (room) => connection.joinRoom(room));\n\n // Register designer-activity polling + the AI's own audit trail\n registerEventsTool(server, () => connection.consumeEvents(), getSessionLog);\n\n // Expose skill documents as MCP prompts (inlined at build time)\n registerPrompts(server);\n\n // Register all domain tools; every plugin command lands in the session log\n registerAllTools(server, async (command, params, timeoutMs) => {\n const t0 = Date.now();\n const nodeId =\n typeof (params as { nodeId?: unknown })?.nodeId === \"string\"\n ? ((params as { nodeId: string }).nodeId)\n : undefined;\n try {\n const result = await connection.sendCommand(command, params, timeoutMs);\n recordCommand({ ts: t0, command, nodeId, ok: true, ms: Date.now() - t0 });\n return result;\n } catch (error) {\n recordCommand({\n ts: t0,\n command,\n nodeId,\n ok: false,\n ms: Date.now() - t0,\n error: error instanceof Error ? error.message.slice(0, 200) : String(error),\n });\n throw error;\n }\n });\n\n // Connect to relay (auto-reconnects on failure)\n try {\n await connection.connect();\n logger.info(\"Connected to relay successfully\");\n } catch (error) {\n logger.warn(\n `Could not connect initially: ${error instanceof Error ? error.message : String(error)}`\n );\n logger.warn(\"Will attempt to connect when the first command is sent\");\n }\n\n // Start MCP server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info(\"Relai MCP server running on stdio\");\n}\n\nmain().catch((error) => {\n logger.error(\n `Fatal error: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n});\n","import WebSocket from \"ws\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { logger } from \"./logger.js\";\nimport { RequestTracker } from \"./request-tracker.js\";\nimport type { FigmaCommand } from \"@figma-relai/shared\";\nimport type { CommandProgressUpdate, RoomSummary } from \"@figma-relai/shared\";\n\nexport interface FigmaConnectionOptions {\n // Room restored from persisted state; used to auto-pair on the first command\n initialRoom?: string | null;\n // Called whenever a room is joined so the caller can persist it\n onRoomChanged?: (room: string) => void;\n // Called before each reconnect attempt (e.g. to take over relay hosting)\n beforeReconnect?: () => Promise<void>;\n}\n\n// Error payload sent by the plugin: a plain string, or an object with context\ninterface PluginErrorPayload {\n message?: string;\n command?: string;\n nodeId?: string;\n nodeType?: string;\n}\n\n// Flatten a plugin error (string or structured object) into one readable line\nexport function formatFigmaError(e: unknown): string {\n if (typeof e === \"string\") return e;\n const { message, command, nodeId, nodeType } = (e ?? {}) as PluginErrorPayload;\n const prefix = command ? `[${command}] ` : \"\";\n const suffix = nodeId\n ? ` (node ${nodeId}${nodeType ? `, type ${nodeType}` : \"\"})`\n : \"\";\n return `${prefix}${message ?? JSON.stringify(e)}${suffix}`;\n}\n\n// Route a response message to its pending request. Error responses have no\n// `result` field, so the error check must come first and resolution must key\n// on field presence — falsy results (0, \"\", false) are valid.\nexport function routeResponse(\n response: { id?: string; result?: unknown; error?: unknown } | undefined,\n tracker: RequestTracker\n): void {\n if (!response?.id || !tracker.has(response.id)) return;\n if (response.error !== undefined) {\n tracker.reject(response.id, new Error(formatFigmaError(response.error)));\n } else if (\"result\" in response) {\n tracker.resolve(response.id, response.result);\n }\n}\n\n// Manages WebSocket connection to the relay server\nexport class FigmaConnection {\n private ws: WebSocket | null = null;\n private currentRoom: string | null = null;\n private tracker = new RequestTracker();\n private wsUrl: string;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private shouldReconnect = false;\n private connectPromise: Promise<void> | null = null;\n private commandHandler: ((command: string, params: unknown) => void) | null = null;\n private options: FigmaConnectionOptions;\n private relayVerified = false;\n // Presence recorded per room: join-time broadcasts can arrive before\n // currentRoom is updated, so keying by room avoids the ordering race\n private presenceByRoom = new Map<string, boolean>();\n // Designer activity piggybacked on plugin responses (selection changes etc.)\n private eventQueue: unknown[] = [];\n\n constructor(\n serverUrl: string = \"localhost\",\n port: number = 9055,\n options: FigmaConnectionOptions = {}\n ) {\n const protocol = serverUrl === \"localhost\" ? \"ws\" : \"wss\";\n this.wsUrl =\n serverUrl === \"localhost\"\n ? `${protocol}://${serverUrl}:${port}`\n : `${protocol}://${serverUrl}`;\n this.options = options;\n }\n\n // Connect to WebSocket relay server\n connect(): Promise<void> {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n logger.info(\"Already connected to relay\");\n return Promise.resolve();\n }\n\n // If already connecting, return existing promise\n if (this.connectPromise) {\n return this.connectPromise;\n }\n\n logger.info(`Connecting to relay at ${this.wsUrl}...`);\n\n this.connectPromise = new Promise<void>((resolve, reject) => {\n const ws = new WebSocket(this.wsUrl);\n this.ws = ws;\n\n const cleanup = () => {\n this.connectPromise = null;\n };\n\n ws.on(\"open\", () => {\n logger.info(\"Connected to relay server\");\n this.shouldReconnect = true;\n cleanup();\n resolve();\n\n // Verify we reached a Relai relay (and not some other port 9055 app)\n this.relayVerified = false;\n try {\n ws.send(JSON.stringify({ type: \"hello\" }));\n } catch {\n // Send failure surfaces via close/error handlers\n }\n setTimeout(() => {\n if (!this.relayVerified && this.ws === ws) {\n logger.warn(\n \"Connected endpoint did not identify as a figma-relai relay — is another app using the port?\"\n );\n }\n }, 1500);\n\n // Rejoin the previous room after a relay restart / reconnect so the\n // AI doesn't land in a silent \"must join a room\" dead state\n if (this.currentRoom) {\n const room = this.currentRoom;\n this.sendCommand(\"join\", { room }).then(\n () => logger.info(`Rejoined room: ${room}`),\n (err) =>\n logger.warn(\n `Failed to rejoin room ${room}: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n }\n });\n\n ws.on(\"message\", (data: WebSocket.Data) => {\n try {\n const json = JSON.parse(data.toString());\n\n // Relay identity probe response\n if (json.type === \"hello\" && json.server === \"figma-relai\") {\n this.relayVerified = true;\n logger.debug(`Relay verified (version ${json.version})`);\n return;\n }\n\n // Peer presence per room (is the plugin there?)\n if (json.type === \"presence\") {\n if (typeof json.room === \"string\" && Array.isArray(json.peers)) {\n this.presenceByRoom.set(\n json.room,\n json.peers.some((p: { role?: string }) => p.role === \"plugin\")\n );\n }\n return;\n }\n\n // Room listing response (auto-pairing)\n if (json.type === \"list_rooms_result\" && json.id) {\n this.tracker.resolve(json.id, json.rooms ?? []);\n return;\n }\n\n // Handle incoming commands (from plugin UI broadcasts)\n if (json.type === \"broadcast\" && json.message?.command) {\n this.commandHandler?.(json.message.command, json.message.params);\n return;\n }\n\n // Handle progress updates\n if (json.type === \"progress_update\") {\n const progressData = json.message?.data as CommandProgressUpdate;\n const requestId = json.id || \"\";\n\n if (requestId && this.tracker.has(requestId)) {\n this.tracker.resetTimeout(requestId);\n logger.info(\n `Progress: ${progressData.commandType} ${progressData.progress}% - ${progressData.message}`\n );\n }\n return;\n }\n\n // Collect designer-activity events piggybacked on responses\n if (Array.isArray(json.message?.events)) {\n this.eventQueue.push(...json.message.events);\n if (this.eventQueue.length > 50) {\n this.eventQueue = this.eventQueue.slice(-50);\n }\n }\n\n // Handle regular responses\n routeResponse(json.message, this.tracker);\n } catch (error) {\n logger.error(\n `Error parsing message: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n });\n\n ws.on(\"error\", (error) => {\n logger.error(`WebSocket error: ${error.message}`);\n cleanup();\n reject(error);\n });\n\n ws.on(\"close\", () => {\n logger.info(\"Disconnected from relay\");\n this.ws = null;\n this.tracker.rejectAll(\"Connection closed\");\n cleanup();\n\n // Only auto-reconnect if not manually disconnected. beforeReconnect\n // lets the host process try to re-bind the relay port first (host\n // takeover when the previous hosting MCP server exited).\n if (this.shouldReconnect) {\n this.reconnectTimer = setTimeout(async () => {\n try {\n await this.options.beforeReconnect?.();\n } catch {\n // Takeover failure is fine; we may connect to another host\n }\n this.connect().catch(() => {});\n }, 2000);\n }\n });\n });\n\n return this.connectPromise;\n }\n\n // Disconnect and stop reconnection\n disconnect(): void {\n this.shouldReconnect = false;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n // Join a room for communication\n async joinRoom(roomName: string): Promise<void> {\n await this.sendCommand(\"join\", { room: roomName });\n this.currentRoom = roomName;\n this.options.onRoomChanged?.(roomName);\n logger.info(`Joined room: ${roomName}`);\n }\n\n // Ask the relay which rooms exist and who is in them\n listRooms(): Promise<RoomSummary[]> {\n return new Promise((resolve, reject) => {\n const id = uuidv4();\n this.tracker.add(id, resolve as (value: unknown) => void, reject, 5000);\n this.ws!.send(JSON.stringify({ type: \"list_rooms\", id }));\n });\n }\n\n // Auto-pair with the plugin so join_room is only needed for disambiguation:\n // prefer the persisted room if its plugin is present, otherwise join the\n // single plugin room, otherwise explain exactly what to do.\n private async ensureRoom(): Promise<void> {\n if (this.currentRoom) return;\n\n const rooms = await this.listRooms();\n const withPlugin = rooms.filter((r) => r.hasPlugin);\n const saved = this.options.initialRoom;\n\n if (saved && withPlugin.some((r) => r.room === saved)) {\n return this.joinRoom(saved);\n }\n if (withPlugin.length === 1) {\n return this.joinRoom(withPlugin[0].room);\n }\n if (withPlugin.length === 0) {\n throw new Error(\n \"No Figma plugin is connected. Open the Relai plugin in Figma (it connects automatically), then try again.\"\n );\n }\n throw new Error(\n `Multiple Figma plugins are connected: ${withPlugin\n .map((r) => (r.fileName ? `\"${r.fileName}\" (room ${r.room})` : `room ${r.room}`))\n .join(\", \")}. Call join_room with the room of the file you want to control.`\n );\n }\n\n // Send a command to Figma plugin via the relay\n async sendCommand(\n command: FigmaCommand,\n params: unknown = {},\n timeoutMs: number = 30000\n ): Promise<unknown> {\n // Wait for connection if not connected\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n try {\n await this.connect();\n } catch {\n // The relay host may have just exited (port-handoff race on rapid\n // server restarts): try to take over hosting, then dial once more.\n try {\n await this.options.beforeReconnect?.();\n } catch {\n // Takeover failure is fine; the retry below may reach another host\n }\n try {\n await this.connect();\n } catch {\n throw new Error(\n \"Not connected to the relay. The MCP server hosts it automatically — if this persists, another app may be occupying the port.\"\n );\n }\n }\n }\n\n if (command !== \"join\") {\n await this.ensureRoom();\n // Presence said the room has no plugin — fail fast, not a 30s timeout\n if (this.currentRoom && this.presenceByRoom.get(this.currentRoom) === false) {\n throw new Error(\n \"The Figma plugin is not open. Open the Relai plugin in Figma — it will reconnect to the same room automatically.\"\n );\n }\n }\n\n const result = await new Promise((resolve, reject) => {\n const id = uuidv4();\n const request = {\n id,\n type: command === \"join\" ? \"join\" : \"message\",\n room:\n command === \"join\"\n ? (params as { room: string }).room\n : this.currentRoom,\n ...(command === \"join\"\n ? { role: \"agent\", meta: { client: \"figma-relai-mcp\" } }\n : {}),\n message: {\n id,\n command,\n params: {\n ...(params as Record<string, unknown>),\n commandId: id,\n },\n },\n };\n\n this.tracker.add(id, resolve, reject, timeoutMs);\n\n logger.info(`Sending command: ${command}`);\n logger.debug(`Request: ${JSON.stringify(request)}`);\n this.ws!.send(JSON.stringify(request));\n });\n\n // Surface designer activity alongside object results so the AI sees it\n // without polling; get_events covers the remaining cases\n if (\n this.eventQueue.length > 0 &&\n result !== null &&\n typeof result === \"object\" &&\n !Array.isArray(result)\n ) {\n return { ...(result as Record<string, unknown>), designer_events: this.consumeEvents() };\n }\n return result;\n }\n\n // Drain buffered designer-activity events\n consumeEvents(): unknown[] {\n const drained = this.eventQueue;\n this.eventQueue = [];\n return drained;\n }\n\n // Set handler for incoming commands from other room members (e.g. plugin UI)\n setCommandHandler(handler: (command: string, params: unknown) => void): void {\n this.commandHandler = handler;\n }\n\n get isConnected(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN;\n }\n\n get room(): string | null {\n return this.currentRoom;\n }\n}\n","// Logger that writes to stderr to keep stdout clean for MCP protocol\n\nexport const logger = {\n info: (message: string) => process.stderr.write(`[INFO] ${message}\\n`),\n debug: (message: string) => process.stderr.write(`[DEBUG] ${message}\\n`),\n warn: (message: string) => process.stderr.write(`[WARN] ${message}\\n`),\n error: (message: string) => process.stderr.write(`[ERROR] ${message}\\n`),\n};\n","import { logger } from \"./logger.js\";\n\n// Pending request entry with resolve/reject callbacks and timeout tracking\nexport interface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n timeout: ReturnType<typeof setTimeout>;\n lastActivity: number;\n}\n\n// Manages pending command requests with timeout and progress update support\nexport class RequestTracker {\n private pending = new Map<string, PendingRequest>();\n\n // Register a new pending request with timeout\n add(\n id: string,\n resolve: (value: unknown) => void,\n reject: (reason: unknown) => void,\n timeoutMs: number = 30000\n ): void {\n const timeout = setTimeout(() => {\n if (this.pending.has(id)) {\n this.pending.delete(id);\n logger.error(`Request ${id} timed out after ${timeoutMs / 1000}s`);\n reject(new Error(\"Request to Figma timed out\"));\n }\n }, timeoutMs);\n\n this.pending.set(id, {\n resolve,\n reject,\n timeout,\n lastActivity: Date.now(),\n });\n }\n\n // Resolve a request with result\n resolve(id: string, result: unknown): boolean {\n const request = this.pending.get(id);\n if (!request) return false;\n\n clearTimeout(request.timeout);\n request.resolve(result);\n this.pending.delete(id);\n return true;\n }\n\n // Reject a request with error\n reject(id: string, error: Error): boolean {\n const request = this.pending.get(id);\n if (!request) return false;\n\n clearTimeout(request.timeout);\n request.reject(error);\n this.pending.delete(id);\n return true;\n }\n\n // Reset timeout on progress update to prevent timeout during long operations\n resetTimeout(id: string, extendedTimeoutMs: number = 60000): void {\n const request = this.pending.get(id);\n if (!request) return;\n\n request.lastActivity = Date.now();\n clearTimeout(request.timeout);\n request.timeout = setTimeout(() => {\n if (this.pending.has(id)) {\n this.pending.delete(id);\n logger.error(`Request ${id} timed out after extended inactivity`);\n request.reject(new Error(\"Request to Figma timed out\"));\n }\n }, extendedTimeoutMs);\n }\n\n // Check if a request is pending\n has(id: string): boolean {\n return this.pending.has(id);\n }\n\n // Reject all pending requests (e.g., on connection close)\n rejectAll(reason: string): void {\n for (const [id, request] of this.pending.entries()) {\n clearTimeout(request.timeout);\n request.reject(new Error(reason));\n }\n this.pending.clear();\n }\n\n get size(): number {\n return this.pending.size;\n }\n}\n","import { createServer } from \"node:http\";\nimport { WebSocketServer, type WebSocket } from \"ws\";\nimport { RelayCore } from \"@figma-relai/shared\";\nimport { logger } from \"./logger.js\";\n\nexport interface EmbeddedRelay {\n close(): void;\n}\n\n// Host the relay in-process on 127.0.0.1:port. Returns null if the port is\n// already taken — another MCP server instance (e.g. Cursor and Claude Code\n// open at once) is hosting, and this process should just connect as a client.\nexport function startEmbeddedRelay(\n port: number,\n version: string\n): Promise<EmbeddedRelay | null> {\n return new Promise((resolve) => {\n const httpServer = createServer();\n\n httpServer.once(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n resolve(null);\n } else {\n logger.warn(`Embedded relay failed to start: ${err.message}`);\n resolve(null);\n }\n });\n\n httpServer.listen(port, \"127.0.0.1\", () => {\n const core = new RelayCore<WebSocket>({\n version,\n log: (msg) => logger.debug(`[relay] ${msg}`),\n });\n const wss = new WebSocketServer({ server: httpServer });\n\n wss.on(\"connection\", (ws) => {\n core.handleOpen(ws);\n ws.on(\"message\", (data) => core.handleMessage(ws, data.toString()));\n ws.on(\"close\", () => core.handleClose(ws));\n ws.on(\"error\", () => {});\n });\n\n core.startStaleCleanup();\n logger.info(`Hosting embedded relay on 127.0.0.1:${port}`);\n\n resolve({\n close() {\n core.stop();\n wss.close();\n httpServer.close();\n },\n });\n });\n });\n}\n","// Ring buffer of every command this server sent to the plugin — the AI's own\n// audit trail, surfaced through get_events (scope: \"agent\"). Complements the\n// plugin's activity feed: the designer sees it live, the AI can review it.\n\nexport interface SessionLogEntry {\n ts: number;\n command: string;\n nodeId?: string;\n ok: boolean;\n ms: number;\n error?: string;\n}\n\nconst MAX_ENTRIES = 200;\nlet entries: SessionLogEntry[] = [];\n\nexport function recordCommand(entry: SessionLogEntry): void {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries = entries.slice(-MAX_ENTRIES);\n}\n\nexport function getSessionLog(): SessionLogEntry[] {\n return [...entries];\n}\n\nexport function clearSessionLog(): void {\n entries = [];\n}\n"],"mappings":"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","names":["serverUrl","port","createServer","port","createServer"]}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
registerEventsTool,
|
|
9
9
|
registerPrompts,
|
|
10
10
|
registerRoomTool
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RDSEEOKP.js";
|
|
12
12
|
import "./chunk-2H7UOFLK.js";
|
|
13
13
|
|
|
14
14
|
// src/cli/manifest.ts
|
|
@@ -52,4 +52,4 @@ async function buildManifest(version) {
|
|
|
52
52
|
export {
|
|
53
53
|
buildManifest
|
|
54
54
|
};
|
|
55
|
-
//# sourceMappingURL=manifest-
|
|
55
|
+
//# sourceMappingURL=manifest-HMHQAF6T.js.map
|