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.
- package/dist/index.js +210 -8
- 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.
|
|
25360
|
+
{ name: "faux-studio", version: "0.3.2" },
|
|
25308
25361
|
{ capabilities: { tools: { listChanged: true } } }
|
|
25309
25362
|
);
|
|
25310
25363
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
25311
|
-
const
|
|
25312
|
-
|
|
25313
|
-
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
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
|
}
|