@sxl-studio/bridge 1.2.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.
Files changed (56) hide show
  1. package/README.md +153 -0
  2. package/dist/bridge-version.d.ts +4 -0
  3. package/dist/bridge-version.js +18 -0
  4. package/dist/bridge-version.js.map +1 -0
  5. package/dist/command-http.d.ts +13 -0
  6. package/dist/command-http.js +29 -0
  7. package/dist/command-http.js.map +1 -0
  8. package/dist/command-queue.d.ts +29 -0
  9. package/dist/command-queue.js +168 -0
  10. package/dist/command-queue.js.map +1 -0
  11. package/dist/http-api.d.ts +8 -0
  12. package/dist/http-api.js +146 -0
  13. package/dist/http-api.js.map +1 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.js +35 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/mcp-factory.d.ts +7 -0
  18. package/dist/mcp-factory.js +30 -0
  19. package/dist/mcp-factory.js.map +1 -0
  20. package/dist/mcp-server.d.ts +9 -0
  21. package/dist/mcp-server.js +16 -0
  22. package/dist/mcp-server.js.map +1 -0
  23. package/dist/session.d.ts +22 -0
  24. package/dist/session.js +64 -0
  25. package/dist/session.js.map +1 -0
  26. package/dist/sxl-mcp-instructions.d.ts +5 -0
  27. package/dist/sxl-mcp-instructions.js +36 -0
  28. package/dist/sxl-mcp-instructions.js.map +1 -0
  29. package/dist/tools/composition.d.ts +6 -0
  30. package/dist/tools/composition.js +47 -0
  31. package/dist/tools/composition.js.map +1 -0
  32. package/dist/tools/data.d.ts +6 -0
  33. package/dist/tools/data.js +56 -0
  34. package/dist/tools/data.js.map +1 -0
  35. package/dist/tools/diagnostics.d.ts +6 -0
  36. package/dist/tools/diagnostics.js +70 -0
  37. package/dist/tools/diagnostics.js.map +1 -0
  38. package/dist/tools/figma-nodes.d.ts +9 -0
  39. package/dist/tools/figma-nodes.js +347 -0
  40. package/dist/tools/figma-nodes.js.map +1 -0
  41. package/dist/tools/figma-rc-extended.d.ts +6 -0
  42. package/dist/tools/figma-rc-extended.js +54 -0
  43. package/dist/tools/figma-rc-extended.js.map +1 -0
  44. package/dist/tools/git.d.ts +6 -0
  45. package/dist/tools/git.js +40 -0
  46. package/dist/tools/git.js.map +1 -0
  47. package/dist/tools/tokens.d.ts +6 -0
  48. package/dist/tools/tokens.js +66 -0
  49. package/dist/tools/tokens.js.map +1 -0
  50. package/dist/types.d.ts +44 -0
  51. package/dist/types.js +5 -0
  52. package/dist/types.js.map +1 -0
  53. package/dist/ws-server.d.ts +31 -0
  54. package/dist/ws-server.js +120 -0
  55. package/dist/ws-server.js.map +1 -0
  56. package/package.json +36 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared types for the Bridge server.
3
+ */
4
+ export type CommandStatus = "pending" | "running" | "completed" | "failed" | "timeout" | "cancelled";
5
+ export type BridgeCommand = {
6
+ commandId: string;
7
+ commandType: string;
8
+ payload: Record<string, unknown>;
9
+ status: CommandStatus;
10
+ createdAt: number;
11
+ startedAt?: number;
12
+ completedAt?: number;
13
+ result?: unknown;
14
+ error?: string;
15
+ events?: unknown[];
16
+ durationMs?: number;
17
+ resolve?: (result: CommandResult) => void;
18
+ };
19
+ export type CommandResult = {
20
+ commandId: string;
21
+ status: CommandStatus;
22
+ result?: unknown;
23
+ error?: string;
24
+ events?: unknown[];
25
+ durationMs?: number;
26
+ };
27
+ export type SessionInfo = {
28
+ connected: boolean;
29
+ pluginName: string | null;
30
+ connectedAt: number | null;
31
+ lastHeartbeat: number | null;
32
+ };
33
+ export type BridgeStatus = {
34
+ /** Single port: plugin WebSocket, HTTP REST, and MCP `/mcp` (same numeric value twice for older clients). */
35
+ ports: {
36
+ websocket: number;
37
+ http: number;
38
+ };
39
+ /** Cursor / MCP clients: Streamable HTTP endpoint (same host as REST, path `/mcp`). */
40
+ mcpStreamableHttp: string;
41
+ session: SessionInfo;
42
+ queueLength: number;
43
+ currentCommand: string | null;
44
+ };
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared types for the Bridge server.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * WebSocket server for Bridge.
3
+ *
4
+ * Accepts a single plugin connection. Receives commands from the CommandQueue
5
+ * and forwards results back from the plugin.
6
+ *
7
+ * Shares one HTTP server with REST + MCP (see http-api.ts): same BRIDGE_PORT for
8
+ * `ws://127.0.0.1:PORT` upgrades and `http://127.0.0.1:PORT/api/*`, `/mcp`.
9
+ */
10
+ import type { Server } from "node:http";
11
+ import { CommandQueue } from "./command-queue.js";
12
+ import { SessionManager } from "./session.js";
13
+ export declare class BridgeWSServer {
14
+ private wss;
15
+ private httpServer;
16
+ private pluginSocket;
17
+ readonly commandQueue: CommandQueue;
18
+ readonly session: SessionManager;
19
+ /**
20
+ * Attach plugin WebSocket handling to an existing HTTP server (same port as REST + MCP).
21
+ */
22
+ attachToHttpServer(httpServer: Server): void;
23
+ /**
24
+ * Drop the plugin WebSocket, clear session, and cancel queued commands.
25
+ * Used after heartbeat timeout, HTTP POST /api/disconnect, or manual recovery.
26
+ */
27
+ disconnectPluginSocket(code: number, reason: string): void;
28
+ private sendToPlugin;
29
+ private handlePluginMessage;
30
+ stop(): void;
31
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * WebSocket server for Bridge.
3
+ *
4
+ * Accepts a single plugin connection. Receives commands from the CommandQueue
5
+ * and forwards results back from the plugin.
6
+ *
7
+ * Shares one HTTP server with REST + MCP (see http-api.ts): same BRIDGE_PORT for
8
+ * `ws://127.0.0.1:PORT` upgrades and `http://127.0.0.1:PORT/api/*`, `/mcp`.
9
+ */
10
+ import { WebSocketServer, WebSocket } from "ws";
11
+ import { CommandQueue } from "./command-queue.js";
12
+ import { SessionManager } from "./session.js";
13
+ export class BridgeWSServer {
14
+ wss = null;
15
+ httpServer = null;
16
+ pluginSocket = null;
17
+ commandQueue = new CommandQueue();
18
+ session = new SessionManager();
19
+ /**
20
+ * Attach plugin WebSocket handling to an existing HTTP server (same port as REST + MCP).
21
+ */
22
+ attachToHttpServer(httpServer) {
23
+ this.httpServer = httpServer;
24
+ this.commandQueue.setSender((cmd) => this.sendToPlugin(cmd));
25
+ this.session.onDisconnect(() => {
26
+ this.disconnectPluginSocket(4002, "Heartbeat timeout");
27
+ });
28
+ this.wss = new WebSocketServer({ server: httpServer });
29
+ this.wss.on("error", (err) => {
30
+ console.error("[WS] WebSocketServer error:", err.message);
31
+ });
32
+ this.wss.on("connection", (ws) => {
33
+ if (this.pluginSocket) {
34
+ ws.close(4001, "Only one plugin connection allowed");
35
+ return;
36
+ }
37
+ this.pluginSocket = ws;
38
+ const addr = httpServer.address();
39
+ const port = addr && typeof addr === "object" ? addr.port : "?";
40
+ console.error(`[WS] Plugin connected (ws://127.0.0.1:${port})`);
41
+ ws.on("message", (data) => {
42
+ try {
43
+ const msg = JSON.parse(data.toString());
44
+ this.handlePluginMessage(msg);
45
+ }
46
+ catch {
47
+ console.warn("[WS] Failed to parse plugin message");
48
+ }
49
+ });
50
+ ws.on("close", () => {
51
+ console.error("[WS] Plugin disconnected");
52
+ if (this.pluginSocket === ws) {
53
+ this.pluginSocket = null;
54
+ }
55
+ this.session.disconnect();
56
+ this.commandQueue.cancelAll();
57
+ });
58
+ ws.on("error", (err) => {
59
+ console.error("[WS] Plugin socket error:", err.message);
60
+ });
61
+ });
62
+ }
63
+ /**
64
+ * Drop the plugin WebSocket, clear session, and cancel queued commands.
65
+ * Used after heartbeat timeout, HTTP POST /api/disconnect, or manual recovery.
66
+ */
67
+ disconnectPluginSocket(code, reason) {
68
+ const sock = this.pluginSocket;
69
+ this.pluginSocket = null;
70
+ this.session.disconnect();
71
+ this.commandQueue.cancelAll();
72
+ if (sock &&
73
+ (sock.readyState === WebSocket.OPEN || sock.readyState === WebSocket.CONNECTING)) {
74
+ sock.close(code, reason);
75
+ }
76
+ }
77
+ sendToPlugin(data) {
78
+ if (!this.pluginSocket || this.pluginSocket.readyState !== WebSocket.OPEN) {
79
+ return false;
80
+ }
81
+ this.pluginSocket.send(JSON.stringify({ type: "command", ...data }));
82
+ return true;
83
+ }
84
+ handlePluginMessage(msg) {
85
+ switch (msg.type) {
86
+ case "plugin-connected":
87
+ this.session.connect(msg.pluginName);
88
+ break;
89
+ case "heartbeat":
90
+ // If the HTTP layer cleared the session but the socket stayed open (legacy bug),
91
+ // or /api/disconnect raced, re-attach the session so POST /api/command works again.
92
+ if (!this.session.connected) {
93
+ this.session.connect(typeof msg.pluginName === "string" ? msg.pluginName : undefined);
94
+ }
95
+ this.session.heartbeat();
96
+ if (this.pluginSocket?.readyState === WebSocket.OPEN) {
97
+ this.pluginSocket.send(JSON.stringify({ type: "heartbeat-ack" }));
98
+ }
99
+ break;
100
+ case "command-result":
101
+ this.commandQueue.resolveCommand(msg.commandId, msg.status, msg.result, msg.error, msg.events, msg.durationMs);
102
+ break;
103
+ case "plugin-event":
104
+ break;
105
+ default:
106
+ break;
107
+ }
108
+ }
109
+ stop() {
110
+ this.pluginSocket?.close();
111
+ this.pluginSocket = null;
112
+ this.session.disconnect();
113
+ this.commandQueue.cancelAll();
114
+ this.wss?.close();
115
+ this.wss = null;
116
+ this.httpServer?.close();
117
+ this.httpServer = null;
118
+ }
119
+ }
120
+ //# sourceMappingURL=ws-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-server.js","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@sxl-studio/bridge",
3
+ "version": "1.2.0",
4
+ "description": "MCP/HTTP bridge server for SXL Studio Figma plugin — enables remote control from IDE",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "sxl-bridge": "dist/index.js"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsc --watch",
20
+ "start": "node dist/index.js",
21
+ "test": "vitest run",
22
+ "lint": "tsc --noEmit",
23
+ "prepublishOnly": "npm run build && npm test"
24
+ },
25
+ "dependencies": {
26
+ "@modelcontextprotocol/sdk": "^1.12.1",
27
+ "ws": "^8.18.0",
28
+ "zod": "^3.24.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^22.0.0",
32
+ "@types/ws": "^8.5.0",
33
+ "typescript": "^5.7.0",
34
+ "vitest": "^3.0.0"
35
+ }
36
+ }