faux-studio 0.3.0 → 0.3.2

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 (2) hide show
  1. package/dist/index.js +210 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ const require = createRequire(import.meta.url);
2
4
  var __create = Object.create;
3
5
  var __defProp = Object.defineProperty;
4
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -11027,6 +11029,57 @@ var FIGMA_DESIGN_URL_RE = /figma\.com\/(file|design)\//;
11027
11029
  function findFigmaDesignTarget(targets) {
11028
11030
  return targets.find((t) => t.type === "page" && FIGMA_DESIGN_URL_RE.test(t.url)) || null;
11029
11031
  }
11032
+ async function probeCdpPorts() {
11033
+ for (const port of KNOWN_CDP_PORTS) {
11034
+ const { alive, isFigma } = await isCdpAlive(port);
11035
+ if (alive && isFigma) {
11036
+ const targets = await listTargets(port);
11037
+ return { port, targets };
11038
+ }
11039
+ }
11040
+ return null;
11041
+ }
11042
+ function killFigma() {
11043
+ try {
11044
+ if (process.platform === "darwin") {
11045
+ execSync("pkill -x Figma", { stdio: "ignore" });
11046
+ return true;
11047
+ }
11048
+ if (process.platform === "win32") {
11049
+ execSync("taskkill /IM Figma.exe", { stdio: "ignore" });
11050
+ return true;
11051
+ }
11052
+ execSync("pkill -x figma-linux", { stdio: "ignore" });
11053
+ return true;
11054
+ } catch {
11055
+ return false;
11056
+ }
11057
+ }
11058
+ async function launchFigmaWithCdp() {
11059
+ const figmaPath = findFigmaPath();
11060
+ if (!figmaPath) {
11061
+ throw new Error(
11062
+ "Figma Desktop is not installed. Download from https://figma.com/downloads"
11063
+ );
11064
+ }
11065
+ const port = await findAvailablePort();
11066
+ log(`Launching Figma Desktop (port ${port})...`);
11067
+ launchFigmaProcess(figmaPath, port);
11068
+ const startTime = Date.now();
11069
+ while (Date.now() - startTime < CDP_WAIT_TIMEOUT_MS) {
11070
+ await new Promise((r) => setTimeout(r, CDP_POLL_INTERVAL_MS));
11071
+ const { alive, isFigma } = await isCdpAlive(port);
11072
+ if (alive && isFigma) {
11073
+ await new Promise((r) => setTimeout(r, 2e3));
11074
+ const targets = await listTargets(port);
11075
+ log("Figma Desktop started");
11076
+ return { port, targets };
11077
+ }
11078
+ }
11079
+ throw new Error(
11080
+ `Figma did not respond on port ${port} within ${CDP_WAIT_TIMEOUT_MS / 1e3}s. Try again.`
11081
+ );
11082
+ }
11030
11083
  function launchFigmaProcess(figmaPath, port) {
11031
11084
  if (process.platform === "darwin") {
11032
11085
  spawn2("open", ["-a", figmaPath, "--args", `--remote-debugging-port=${port}`], {
@@ -25304,16 +25357,32 @@ async function handleGetBlueprint(blueprintId, jwt2) {
25304
25357
  }
25305
25358
  function createMcpServer(deps) {
25306
25359
  const server = new Server(
25307
- { name: "faux-studio", version: "0.3.0" },
25360
+ { name: "faux-studio", version: "0.3.2" },
25308
25361
  { capabilities: { tools: { listChanged: true } } }
25309
25362
  );
25310
25363
  server.setRequestHandler(ListToolsRequestSchema, async () => {
25311
- const loginTool = {
25312
- name: "login",
25313
- description: "Check authentication status and re-authenticate if needed. Use when you get auth errors from other tools.",
25314
- inputSchema: { type: "object", properties: {}, required: [] }
25315
- };
25316
- return { tools: [loginTool, ...deps.tools] };
25364
+ const localTools = [
25365
+ {
25366
+ name: "login",
25367
+ description: "Check authentication status and re-authenticate if needed. Use when you get auth errors from other tools.",
25368
+ inputSchema: { type: "object", properties: {}, required: [] }
25369
+ },
25370
+ {
25371
+ name: "setup_figma",
25372
+ description: "Ensure Figma Desktop is running and connected. Call this before any design work. Checks plugin and CDP transports, launches Figma if needed, or restarts it with the debug port. Idempotent \u2014 safe to call multiple times. Returns connection status and active file info.",
25373
+ inputSchema: {
25374
+ type: "object",
25375
+ properties: {
25376
+ force_restart: {
25377
+ type: "boolean",
25378
+ description: "Restart Figma even if it is already running (e.g., to enable the debug port). Figma auto-recovers open files on restart. Only set this if instructed or if the previous call returned needs_restart."
25379
+ }
25380
+ },
25381
+ required: []
25382
+ }
25383
+ }
25384
+ ];
25385
+ return { tools: [...localTools, ...deps.tools] };
25317
25386
  });
25318
25387
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
25319
25388
  const { name, arguments: args } = request.params;
@@ -25365,6 +25434,16 @@ function createMcpServer(deps) {
25365
25434
  }
25366
25435
  }
25367
25436
  }
25437
+ if (name === "setup_figma") {
25438
+ log("setup_figma called");
25439
+ const result2 = await deps.setupFigma({
25440
+ force_restart: params.force_restart === true
25441
+ });
25442
+ return {
25443
+ content: [{ type: "text", text: JSON.stringify(result2, null, 2) }],
25444
+ isError: result2.status === "not_installed"
25445
+ };
25446
+ }
25368
25447
  if (name === "get_blueprint") {
25369
25448
  const blueprintId = params.blueprintId;
25370
25449
  if (!blueprintId) throw new Error("Missing required parameter: blueprintId");
@@ -25500,6 +25579,128 @@ async function generateWithAuth(toolName, params) {
25500
25579
  throw err;
25501
25580
  }
25502
25581
  }
25582
+ async function setupFigma(params) {
25583
+ if (forceTransport !== "cdp" && pluginServer.hasConnections) {
25584
+ return {
25585
+ status: "ready",
25586
+ transport: "plugin",
25587
+ message: `Connected via plugin.${pluginServer.activeFileId ? ` Active file: ${pluginServer.activeFileId}` : ""} Ready to design.`,
25588
+ activeFile: pluginServer.activeFileId || void 0,
25589
+ pluginFiles: pluginServer.connectedFiles,
25590
+ port: pluginServer.port
25591
+ };
25592
+ }
25593
+ if (forceTransport !== "plugin" && cdpClient?.connected && cdpClient.hasContext) {
25594
+ return {
25595
+ status: "ready",
25596
+ transport: "cdp",
25597
+ message: "Connected via CDP. Ready to design."
25598
+ };
25599
+ }
25600
+ if (forceTransport !== "plugin") {
25601
+ const existing = await probeCdpPorts();
25602
+ if (existing) {
25603
+ const target = findFigmaDesignTarget(existing.targets);
25604
+ if (target) {
25605
+ try {
25606
+ cdpClient?.close();
25607
+ cdpClient = null;
25608
+ const client = new CdpClient();
25609
+ await client.connect(target.webSocketDebuggerUrl);
25610
+ await client.discoverFigmaContext();
25611
+ cdpClient = client;
25612
+ log(`CDP connected: ${target.title}`);
25613
+ return {
25614
+ status: "ready",
25615
+ transport: "cdp",
25616
+ message: `Connected via CDP. Active file: ${target.title}. Ready to design.`,
25617
+ activeFile: target.title,
25618
+ port: existing.port
25619
+ };
25620
+ } catch {
25621
+ }
25622
+ }
25623
+ return {
25624
+ status: "no_design_file",
25625
+ transport: "none",
25626
+ message: "Figma is running with the debug port, but no design file is open. Ask the user to open a .fig file in Figma, then call setup_figma again.",
25627
+ port: existing.port
25628
+ };
25629
+ }
25630
+ }
25631
+ if (isFigmaRunning()) {
25632
+ if (params.force_restart) {
25633
+ log("Restarting Figma with debug port...");
25634
+ killFigma();
25635
+ await new Promise((r) => setTimeout(r, 3e3));
25636
+ try {
25637
+ const conn = await launchFigmaWithCdp();
25638
+ const target = findFigmaDesignTarget(conn.targets);
25639
+ if (target) {
25640
+ try {
25641
+ const client = new CdpClient();
25642
+ await client.connect(target.webSocketDebuggerUrl);
25643
+ await client.discoverFigmaContext();
25644
+ cdpClient = client;
25645
+ } catch {
25646
+ }
25647
+ }
25648
+ return {
25649
+ status: "restarted",
25650
+ transport: target ? "cdp" : "none",
25651
+ message: target ? `Figma restarted with debug port. Connected to: ${target.title}. Ready to design.` : "Figma restarted with debug port. Waiting for a design file to be opened. Call setup_figma again once a file is open.",
25652
+ activeFile: target?.title,
25653
+ port: conn.port
25654
+ };
25655
+ } catch (err) {
25656
+ return {
25657
+ status: "not_installed",
25658
+ transport: "none",
25659
+ message: err instanceof Error ? err.message : "Failed to restart Figma."
25660
+ };
25661
+ }
25662
+ }
25663
+ return {
25664
+ status: "needs_restart",
25665
+ transport: "none",
25666
+ message: "Figma is running but the debug port is not enabled. Figma needs to restart with the debug port for AI tools to work. Figma auto-recovers all open files on restart, so no work will be lost. Ask the user for permission, then call setup_figma again with force_restart: true."
25667
+ };
25668
+ }
25669
+ if (!findFigmaPath()) {
25670
+ return {
25671
+ status: "not_installed",
25672
+ transport: "none",
25673
+ message: "Figma Desktop is not installed. Download from https://figma.com/downloads and install it, then call setup_figma again."
25674
+ };
25675
+ }
25676
+ try {
25677
+ log("Launching Figma Desktop...");
25678
+ const conn = await launchFigmaWithCdp();
25679
+ const target = findFigmaDesignTarget(conn.targets);
25680
+ if (target) {
25681
+ try {
25682
+ const client = new CdpClient();
25683
+ await client.connect(target.webSocketDebuggerUrl);
25684
+ await client.discoverFigmaContext();
25685
+ cdpClient = client;
25686
+ } catch {
25687
+ }
25688
+ }
25689
+ return {
25690
+ status: "launched",
25691
+ transport: target ? "cdp" : "none",
25692
+ message: target ? `Figma launched and connected. Active file: ${target.title}. Ready to design.` : "Figma launched. Open a design file to start designing, then call setup_figma again.",
25693
+ activeFile: target?.title,
25694
+ port: conn.port
25695
+ };
25696
+ } catch (err) {
25697
+ return {
25698
+ status: "not_installed",
25699
+ transport: "none",
25700
+ message: err instanceof Error ? err.message : "Failed to launch Figma."
25701
+ };
25702
+ }
25703
+ }
25503
25704
  async function main() {
25504
25705
  try {
25505
25706
  auth = await ensureAuth();
@@ -25557,7 +25758,8 @@ async function main() {
25557
25758
  email: auth.user.email,
25558
25759
  source: auth.source,
25559
25760
  isApiKey: auth.source === "api-key"
25560
- })
25761
+ }),
25762
+ setupFigma
25561
25763
  });
25562
25764
  await startServer(server);
25563
25765
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "faux-studio",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "AI-powered Figma design via MCP — connect any AI client to Figma Desktop",
5
5
  "type": "module",
6
6
  "bin": {