@atezer/figma-mcp-bridge 1.4.0 → 1.4.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/CHANGELOG.md +15 -0
- package/package.json +11 -2
- package/dist/cloudflare/browser/base.js +0 -5
- package/dist/cloudflare/browser/cloudflare.js +0 -156
- package/dist/cloudflare/browser-manager.js +0 -157
- package/dist/cloudflare/core/audit-log.js +0 -62
- package/dist/cloudflare/core/config.js +0 -163
- package/dist/cloudflare/core/console-monitor.js +0 -427
- package/dist/cloudflare/core/design-system-manifest.js +0 -260
- package/dist/cloudflare/core/enrichment/enrichment-service.js +0 -272
- package/dist/cloudflare/core/enrichment/index.js +0 -7
- package/dist/cloudflare/core/enrichment/relationship-mapper.js +0 -351
- package/dist/cloudflare/core/enrichment/style-resolver.js +0 -326
- package/dist/cloudflare/core/figma-api.js +0 -273
- package/dist/cloudflare/core/figma-desktop-connector.js +0 -1041
- package/dist/cloudflare/core/figma-reconstruction-spec.js +0 -402
- package/dist/cloudflare/core/figma-tools.js +0 -2919
- package/dist/cloudflare/core/figma-url.js +0 -48
- package/dist/cloudflare/core/logger.js +0 -53
- package/dist/cloudflare/core/plugin-bridge-connector.js +0 -197
- package/dist/cloudflare/core/plugin-bridge-server.js +0 -375
- package/dist/cloudflare/core/snippet-injector.js +0 -96
- package/dist/cloudflare/core/types/enriched.js +0 -5
- package/dist/cloudflare/core/types/index.js +0 -4
- package/dist/cloudflare/index.js +0 -1061
- package/dist/cloudflare/test-browser.js +0 -88
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,21 @@ Bu dosya [Keep a Changelog](https://keepachangelog.com/tr/1.1.0/) bicimine uygun
|
|
|
12
12
|
|
|
13
13
|
Bu changelog'a ekleme oncesi surumlerin tam ayrintilari icin `git log` kullanilabilir.
|
|
14
14
|
|
|
15
|
+
## [1.4.2] - 2026-04-02
|
|
16
|
+
|
|
17
|
+
### Kritik duzeltme
|
|
18
|
+
|
|
19
|
+
- **dist/browser pakete geri eklendi:** v1.4.1'de tam mod (local.js) `dist/browser/local.js`'i import ediyordu ama dosya paketten cikarilmisti → MODULE_NOT_FOUND hatasi. `dist/cloudflare/` haric tutuldu (bu kullanilmiyor).
|
|
20
|
+
|
|
21
|
+
## [1.4.1] - 2026-04-02 [YANLIS — tam mod kirik, 1.4.2 kullanin]
|
|
22
|
+
|
|
23
|
+
### npm paket optimizasyonu
|
|
24
|
+
|
|
25
|
+
- **Paket boyutu:** 284 KB → 230 KB (%19 kucuk), acik 1.7 MB → 1.2 MB (%30 kucuk)
|
|
26
|
+
- **Dosya sayisi:** 128 → 96 (32 gereksiz dosya cikarildi)
|
|
27
|
+
- **Cikarilan:** `dist/cloudflare/` (440 KB), `dist/browser/` (44 KB) — plugin-only modda kullanilmiyordu
|
|
28
|
+
- **Korunan:** `dist/local.js` (tam mod), `dist/core/` (paylasimli), `f-mcp-plugin/`
|
|
29
|
+
|
|
15
30
|
## [1.4.0] - 2026-04-02
|
|
16
31
|
|
|
17
32
|
### Figma REST API entegrasyonu (YENi)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atezer/figma-mcp-bridge",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "F-MCP ATezer: MCP server and Figma plugin bridge for Claude/Cursor. No REST token required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/local.js",
|
|
@@ -10,7 +10,16 @@
|
|
|
10
10
|
"figma-mcp-bridge-plugin": "./dist/local-plugin-only.js"
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
|
-
"dist",
|
|
13
|
+
"dist/local-plugin-only.js",
|
|
14
|
+
"dist/local-plugin-only.js.map",
|
|
15
|
+
"dist/local-plugin-only.d.ts",
|
|
16
|
+
"dist/local-plugin-only.d.ts.map",
|
|
17
|
+
"dist/local.js",
|
|
18
|
+
"dist/local.js.map",
|
|
19
|
+
"dist/local.d.ts",
|
|
20
|
+
"dist/local.d.ts.map",
|
|
21
|
+
"dist/browser",
|
|
22
|
+
"dist/core",
|
|
14
23
|
"f-mcp-plugin",
|
|
15
24
|
"README.md",
|
|
16
25
|
"CHANGELOG.md",
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cloudflare Browser Manager
|
|
3
|
-
* Manages Puppeteer browser instance lifecycle for Cloudflare Browser Rendering API
|
|
4
|
-
*/
|
|
5
|
-
import puppeteer from '@cloudflare/puppeteer';
|
|
6
|
-
import { createChildLogger } from '../core/logger.js';
|
|
7
|
-
const logger = createChildLogger({ component: 'cloudflare-browser' });
|
|
8
|
-
/**
|
|
9
|
-
* Cloudflare Browser Manager
|
|
10
|
-
* Implements IBrowserManager for Cloudflare Browser Rendering API
|
|
11
|
-
*/
|
|
12
|
-
export class CloudflareBrowserManager {
|
|
13
|
-
constructor(env, config) {
|
|
14
|
-
this.browser = null;
|
|
15
|
-
this.page = null;
|
|
16
|
-
this.env = env;
|
|
17
|
-
this.config = config;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Launch browser instance via Cloudflare Browser Rendering API
|
|
21
|
-
*/
|
|
22
|
-
async launch() {
|
|
23
|
-
if (this.browser) {
|
|
24
|
-
logger.info('Browser already running, reusing instance');
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
logger.info('Launching browser with Cloudflare Browser Rendering API');
|
|
28
|
-
try {
|
|
29
|
-
this.browser = await puppeteer.launch(this.env.BROWSER, {
|
|
30
|
-
keep_alive: 600000, // Keep alive for 10 minutes
|
|
31
|
-
});
|
|
32
|
-
logger.info('Browser launched successfully');
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
logger.error({ error }, 'Failed to launch browser');
|
|
36
|
-
throw new Error(`Browser launch failed: ${error}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Get or create a page instance
|
|
41
|
-
*/
|
|
42
|
-
async getPage() {
|
|
43
|
-
if (!this.browser) {
|
|
44
|
-
await this.launch();
|
|
45
|
-
}
|
|
46
|
-
if (this.page && !this.page.isClosed()) {
|
|
47
|
-
return this.page;
|
|
48
|
-
}
|
|
49
|
-
logger.info('Creating new browser page');
|
|
50
|
-
this.page = await this.browser.newPage();
|
|
51
|
-
// Set viewport size
|
|
52
|
-
await this.page.setViewport({
|
|
53
|
-
width: 1920,
|
|
54
|
-
height: 1080,
|
|
55
|
-
deviceScaleFactor: 1,
|
|
56
|
-
});
|
|
57
|
-
logger.info('Browser page created');
|
|
58
|
-
return this.page;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Navigate to Figma URL
|
|
62
|
-
*/
|
|
63
|
-
async navigateToFigma(figmaUrl) {
|
|
64
|
-
const page = await this.getPage();
|
|
65
|
-
// Default to Figma homepage if no URL provided
|
|
66
|
-
const url = figmaUrl || 'https://www.figma.com';
|
|
67
|
-
logger.info({ url }, 'Navigating to Figma');
|
|
68
|
-
try {
|
|
69
|
-
await page.goto(url, {
|
|
70
|
-
waitUntil: 'networkidle2',
|
|
71
|
-
timeout: 30000,
|
|
72
|
-
});
|
|
73
|
-
logger.info({ url }, 'Navigation successful');
|
|
74
|
-
return page;
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
logger.error({ error, url }, 'Navigation failed');
|
|
78
|
-
throw new Error(`Failed to navigate to ${url}: ${error}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Reload current page
|
|
83
|
-
*/
|
|
84
|
-
async reload(hardReload = false) {
|
|
85
|
-
if (!this.page || this.page.isClosed()) {
|
|
86
|
-
throw new Error('No active page to reload');
|
|
87
|
-
}
|
|
88
|
-
logger.info({ hardReload }, 'Reloading page');
|
|
89
|
-
try {
|
|
90
|
-
await this.page.reload({
|
|
91
|
-
waitUntil: 'networkidle2',
|
|
92
|
-
timeout: 30000,
|
|
93
|
-
});
|
|
94
|
-
logger.info('Page reloaded successfully');
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
logger.error({ error }, 'Page reload failed');
|
|
98
|
-
throw new Error(`Page reload failed: ${error}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Execute JavaScript in page context
|
|
103
|
-
*/
|
|
104
|
-
async evaluate(fn) {
|
|
105
|
-
const page = await this.getPage();
|
|
106
|
-
return page.evaluate(fn);
|
|
107
|
-
}
|
|
108
|
-
// Screenshot functionality removed - use Figma REST API's getImages() instead
|
|
109
|
-
// See: figma_take_screenshot and figma_get_component_image tools
|
|
110
|
-
/**
|
|
111
|
-
* Check if browser is running
|
|
112
|
-
*/
|
|
113
|
-
isRunning() {
|
|
114
|
-
return this.browser !== null && this.browser.isConnected();
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Close browser instance
|
|
118
|
-
*/
|
|
119
|
-
async close() {
|
|
120
|
-
if (!this.browser) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
logger.info('Closing browser');
|
|
124
|
-
try {
|
|
125
|
-
await this.browser.close();
|
|
126
|
-
this.browser = null;
|
|
127
|
-
this.page = null;
|
|
128
|
-
logger.info('Browser closed successfully');
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
logger.error({ error }, 'Failed to close browser');
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Get current page URL
|
|
137
|
-
*/
|
|
138
|
-
getCurrentUrl() {
|
|
139
|
-
if (!this.page || this.page.isClosed()) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
return this.page.url();
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Wait for navigation
|
|
146
|
-
*/
|
|
147
|
-
async waitForNavigation(timeout = 30000) {
|
|
148
|
-
if (!this.page || this.page.isClosed()) {
|
|
149
|
-
throw new Error('No active page');
|
|
150
|
-
}
|
|
151
|
-
await this.page.waitForNavigation({
|
|
152
|
-
waitUntil: 'networkidle2',
|
|
153
|
-
timeout,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser Manager
|
|
3
|
-
* Manages Puppeteer browser instance lifecycle for Cloudflare Browser Rendering API
|
|
4
|
-
*/
|
|
5
|
-
import puppeteer from '@cloudflare/puppeteer';
|
|
6
|
-
import { createChildLogger } from './core/logger.js';
|
|
7
|
-
const logger = createChildLogger({ component: 'browser-manager' });
|
|
8
|
-
/**
|
|
9
|
-
* Browser Manager
|
|
10
|
-
* Handles browser instance creation, page management, and navigation
|
|
11
|
-
*/
|
|
12
|
-
export class BrowserManager {
|
|
13
|
-
constructor(env, config) {
|
|
14
|
-
this.browser = null;
|
|
15
|
-
this.page = null;
|
|
16
|
-
this.env = env;
|
|
17
|
-
this.config = config;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Launch browser instance
|
|
21
|
-
*/
|
|
22
|
-
async launch() {
|
|
23
|
-
if (this.browser) {
|
|
24
|
-
logger.info('Browser already running, reusing instance');
|
|
25
|
-
return this.browser;
|
|
26
|
-
}
|
|
27
|
-
logger.info('Launching browser with Cloudflare Browser Rendering API');
|
|
28
|
-
try {
|
|
29
|
-
this.browser = await puppeteer.launch(this.env.BROWSER, {
|
|
30
|
-
keep_alive: 600000, // Keep alive for 10 minutes
|
|
31
|
-
});
|
|
32
|
-
logger.info('Browser launched successfully');
|
|
33
|
-
return this.browser;
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
logger.error({ error }, 'Failed to launch browser');
|
|
37
|
-
throw new Error(`Browser launch failed: ${error}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Get or create a page instance
|
|
42
|
-
*/
|
|
43
|
-
async getPage() {
|
|
44
|
-
if (!this.browser) {
|
|
45
|
-
await this.launch();
|
|
46
|
-
}
|
|
47
|
-
if (this.page && !this.page.isClosed()) {
|
|
48
|
-
return this.page;
|
|
49
|
-
}
|
|
50
|
-
logger.info('Creating new browser page');
|
|
51
|
-
this.page = await this.browser.newPage();
|
|
52
|
-
// Set viewport size
|
|
53
|
-
await this.page.setViewport({
|
|
54
|
-
width: 1920,
|
|
55
|
-
height: 1080,
|
|
56
|
-
deviceScaleFactor: 1,
|
|
57
|
-
});
|
|
58
|
-
logger.info('Browser page created');
|
|
59
|
-
return this.page;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Navigate to Figma URL
|
|
63
|
-
*/
|
|
64
|
-
async navigateToFigma(figmaUrl) {
|
|
65
|
-
const page = await this.getPage();
|
|
66
|
-
// Default to Figma homepage if no URL provided
|
|
67
|
-
const url = figmaUrl || 'https://www.figma.com';
|
|
68
|
-
logger.info({ url }, 'Navigating to Figma');
|
|
69
|
-
try {
|
|
70
|
-
await page.goto(url, {
|
|
71
|
-
waitUntil: 'networkidle2',
|
|
72
|
-
timeout: 30000,
|
|
73
|
-
});
|
|
74
|
-
logger.info({ url }, 'Navigation successful');
|
|
75
|
-
return page;
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
logger.error({ error, url }, 'Navigation failed');
|
|
79
|
-
throw new Error(`Failed to navigate to ${url}: ${error}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Reload current page
|
|
84
|
-
*/
|
|
85
|
-
async reload(hardReload = false) {
|
|
86
|
-
if (!this.page || this.page.isClosed()) {
|
|
87
|
-
throw new Error('No active page to reload');
|
|
88
|
-
}
|
|
89
|
-
logger.info({ hardReload }, 'Reloading page');
|
|
90
|
-
try {
|
|
91
|
-
await this.page.reload({
|
|
92
|
-
waitUntil: 'networkidle2',
|
|
93
|
-
timeout: 30000,
|
|
94
|
-
});
|
|
95
|
-
logger.info('Page reloaded successfully');
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
logger.error({ error }, 'Page reload failed');
|
|
99
|
-
throw new Error(`Page reload failed: ${error}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Execute JavaScript in page context
|
|
104
|
-
*/
|
|
105
|
-
async evaluate(fn) {
|
|
106
|
-
const page = await this.getPage();
|
|
107
|
-
return page.evaluate(fn);
|
|
108
|
-
}
|
|
109
|
-
// Screenshot functionality removed - use Figma REST API's getImages() instead
|
|
110
|
-
// See: figma_take_screenshot and figma_get_component_image tools
|
|
111
|
-
/**
|
|
112
|
-
* Check if browser is running
|
|
113
|
-
*/
|
|
114
|
-
isRunning() {
|
|
115
|
-
return this.browser !== null && this.browser.isConnected();
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Close browser instance
|
|
119
|
-
*/
|
|
120
|
-
async close() {
|
|
121
|
-
if (!this.browser) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
logger.info('Closing browser');
|
|
125
|
-
try {
|
|
126
|
-
await this.browser.close();
|
|
127
|
-
this.browser = null;
|
|
128
|
-
this.page = null;
|
|
129
|
-
logger.info('Browser closed successfully');
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
logger.error({ error }, 'Failed to close browser');
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Get current page URL
|
|
138
|
-
*/
|
|
139
|
-
getCurrentUrl() {
|
|
140
|
-
if (!this.page || this.page.isClosed()) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
return this.page.url();
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Wait for navigation
|
|
147
|
-
*/
|
|
148
|
-
async waitForNavigation(timeout = 30000) {
|
|
149
|
-
if (!this.page || this.page.isClosed()) {
|
|
150
|
-
throw new Error('No active page');
|
|
151
|
-
}
|
|
152
|
-
await this.page.waitForNavigation({
|
|
153
|
-
waitUntil: 'networkidle2',
|
|
154
|
-
timeout,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise audit log — tool invocations and connection events for compliance.
|
|
3
|
-
* Optional: enable via FIGMA_MCP_AUDIT_LOG_PATH (file path) or config local.auditLogPath.
|
|
4
|
-
* Format: one JSON object per line (NDJSON) for easy parsing and retention.
|
|
5
|
-
*/
|
|
6
|
-
import { createWriteStream } from "fs";
|
|
7
|
-
import { appendFileSync } from "fs";
|
|
8
|
-
const noop = () => { };
|
|
9
|
-
let stream = null;
|
|
10
|
-
function getStream(path) {
|
|
11
|
-
if (stream)
|
|
12
|
-
return stream;
|
|
13
|
-
try {
|
|
14
|
-
stream = createWriteStream(path, { flags: "a" });
|
|
15
|
-
stream.on("error", () => {
|
|
16
|
-
stream = null;
|
|
17
|
-
});
|
|
18
|
-
return stream;
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function writeLine(path, line) {
|
|
25
|
-
const s = getStream(path);
|
|
26
|
-
if (s && s.writable) {
|
|
27
|
-
s.write(line + "\n");
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
appendFileSync(path, line + "\n");
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// ignore
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Log an audit entry. No-op if path not set or write fails.
|
|
39
|
-
*/
|
|
40
|
-
export function auditLog(path, entry) {
|
|
41
|
-
if (!path || path === "")
|
|
42
|
-
return;
|
|
43
|
-
const full = { ...entry, ts: new Date().toISOString() };
|
|
44
|
-
try {
|
|
45
|
-
writeLine(path, JSON.stringify(full));
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
noop();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Log a tool invocation (call from bridge after request completes).
|
|
53
|
-
*/
|
|
54
|
-
export function auditTool(path, method, success, error, durationMs) {
|
|
55
|
-
auditLog(path, { event: "tool", method, success, error, durationMs });
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Log plugin connection / disconnection.
|
|
59
|
-
*/
|
|
60
|
-
export function auditPlugin(path, event) {
|
|
61
|
-
auditLog(path, { event });
|
|
62
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration management for F-MCP ATezer (Figma MCP Bridge) server
|
|
3
|
-
*/
|
|
4
|
-
import { readFileSync, existsSync } from 'fs';
|
|
5
|
-
import { homedir } from 'os';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
/**
|
|
8
|
-
* Auto-detect server mode based on environment
|
|
9
|
-
*/
|
|
10
|
-
function detectMode() {
|
|
11
|
-
// If running in Workers environment, return cloudflare
|
|
12
|
-
if (typeof globalThis !== 'undefined' && 'caches' in globalThis) {
|
|
13
|
-
return 'cloudflare';
|
|
14
|
-
}
|
|
15
|
-
// Explicit env var override
|
|
16
|
-
const modeEnv = process.env.FIGMA_MCP_MODE?.toLowerCase();
|
|
17
|
-
if (modeEnv === 'local' || modeEnv === 'cloudflare') {
|
|
18
|
-
return modeEnv;
|
|
19
|
-
}
|
|
20
|
-
// Default to local for Node.js environments
|
|
21
|
-
return 'local';
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Default configuration values
|
|
25
|
-
*/
|
|
26
|
-
const DEFAULT_CONFIG = {
|
|
27
|
-
mode: detectMode(),
|
|
28
|
-
browser: {
|
|
29
|
-
headless: false,
|
|
30
|
-
args: [
|
|
31
|
-
'--disable-blink-features=AutomationControlled',
|
|
32
|
-
'--disable-dev-shm-usage',
|
|
33
|
-
'--no-sandbox', // Note: Only use in trusted environments
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
console: {
|
|
37
|
-
bufferSize: 1000,
|
|
38
|
-
filterLevels: ['log', 'info', 'warn', 'error', 'debug'],
|
|
39
|
-
truncation: {
|
|
40
|
-
maxStringLength: 500,
|
|
41
|
-
maxArrayLength: 10,
|
|
42
|
-
maxObjectDepth: 3,
|
|
43
|
-
removeDuplicates: true,
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
screenshots: {
|
|
47
|
-
defaultFormat: 'png',
|
|
48
|
-
quality: 90,
|
|
49
|
-
storePath: join(process.env.TMPDIR || '/tmp', 'figma-mcp-bridge', 'screenshots'),
|
|
50
|
-
},
|
|
51
|
-
local: {
|
|
52
|
-
debugHost: process.env.FIGMA_DEBUG_HOST || 'localhost',
|
|
53
|
-
debugPort: parseInt(process.env.FIGMA_DEBUG_PORT || '9222', 10),
|
|
54
|
-
pluginBridgePort: parseInt(process.env.FIGMA_MCP_BRIDGE_PORT || process.env.FIGMA_PLUGIN_BRIDGE_PORT || '5454', 10),
|
|
55
|
-
auditLogPath: process.env.FIGMA_MCP_AUDIT_LOG_PATH || undefined,
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
/**
|
|
59
|
-
* Possible config file locations (checked in order)
|
|
60
|
-
*/
|
|
61
|
-
const CONFIG_PATHS = [
|
|
62
|
-
// Environment variable override
|
|
63
|
-
process.env.FIGMA_CONSOLE_CONFIG,
|
|
64
|
-
// Project-local config
|
|
65
|
-
join(process.cwd(), '.figma-mcp-bridge.json'),
|
|
66
|
-
join(process.cwd(), 'figma-mcp-bridge.json'),
|
|
67
|
-
// User home config
|
|
68
|
-
join(homedir(), '.config', 'figma-mcp-bridge', 'config.json'),
|
|
69
|
-
join(homedir(), '.figma-mcp-bridge.json'),
|
|
70
|
-
].filter((path) => path !== undefined);
|
|
71
|
-
/**
|
|
72
|
-
* Load configuration from file or use defaults
|
|
73
|
-
*/
|
|
74
|
-
export function loadConfig() {
|
|
75
|
-
// Try to load from config file
|
|
76
|
-
for (const configPath of CONFIG_PATHS) {
|
|
77
|
-
if (existsSync(configPath)) {
|
|
78
|
-
try {
|
|
79
|
-
const fileContent = readFileSync(configPath, 'utf-8');
|
|
80
|
-
const userConfig = JSON.parse(fileContent);
|
|
81
|
-
// Deep merge with defaults
|
|
82
|
-
const config = mergeConfig(DEFAULT_CONFIG, userConfig);
|
|
83
|
-
return config;
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
console.error(`Failed to load config from ${configPath}:`, error);
|
|
87
|
-
// Continue to next config path
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// No config file found, use defaults
|
|
92
|
-
return DEFAULT_CONFIG;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Deep merge two configuration objects
|
|
96
|
-
*/
|
|
97
|
-
function mergeConfig(defaults, overrides) {
|
|
98
|
-
return {
|
|
99
|
-
mode: overrides.mode || defaults.mode,
|
|
100
|
-
browser: {
|
|
101
|
-
...defaults.browser,
|
|
102
|
-
...(overrides.browser || {}),
|
|
103
|
-
},
|
|
104
|
-
console: {
|
|
105
|
-
...defaults.console,
|
|
106
|
-
...(overrides.console || {}),
|
|
107
|
-
truncation: {
|
|
108
|
-
...defaults.console.truncation,
|
|
109
|
-
...(overrides.console?.truncation || {}),
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
screenshots: {
|
|
113
|
-
...defaults.screenshots,
|
|
114
|
-
...(overrides.screenshots || {}),
|
|
115
|
-
},
|
|
116
|
-
local: {
|
|
117
|
-
...defaults.local,
|
|
118
|
-
...(overrides.local || {}),
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Validate configuration
|
|
124
|
-
*/
|
|
125
|
-
export function validateConfig(config) {
|
|
126
|
-
// Validate browser config
|
|
127
|
-
if (!Array.isArray(config.browser.args)) {
|
|
128
|
-
throw new Error('browser.args must be an array');
|
|
129
|
-
}
|
|
130
|
-
// Validate console config
|
|
131
|
-
if (config.console.bufferSize <= 0) {
|
|
132
|
-
throw new Error('console.bufferSize must be positive');
|
|
133
|
-
}
|
|
134
|
-
if (!Array.isArray(config.console.filterLevels)) {
|
|
135
|
-
throw new Error('console.filterLevels must be an array');
|
|
136
|
-
}
|
|
137
|
-
// Validate truncation config
|
|
138
|
-
const { truncation } = config.console;
|
|
139
|
-
if (truncation.maxStringLength <= 0) {
|
|
140
|
-
throw new Error('console.truncation.maxStringLength must be positive');
|
|
141
|
-
}
|
|
142
|
-
if (truncation.maxArrayLength <= 0) {
|
|
143
|
-
throw new Error('console.truncation.maxArrayLength must be positive');
|
|
144
|
-
}
|
|
145
|
-
if (truncation.maxObjectDepth <= 0) {
|
|
146
|
-
throw new Error('console.truncation.maxObjectDepth must be positive');
|
|
147
|
-
}
|
|
148
|
-
// Validate screenshot config
|
|
149
|
-
if (!['png', 'jpeg'].includes(config.screenshots.defaultFormat)) {
|
|
150
|
-
throw new Error('screenshots.defaultFormat must be "png" or "jpeg"');
|
|
151
|
-
}
|
|
152
|
-
if (config.screenshots.quality < 0 || config.screenshots.quality > 100) {
|
|
153
|
-
throw new Error('screenshots.quality must be between 0 and 100');
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Get configuration with validation
|
|
158
|
-
*/
|
|
159
|
-
export function getConfig() {
|
|
160
|
-
const config = loadConfig();
|
|
161
|
-
validateConfig(config);
|
|
162
|
-
return config;
|
|
163
|
-
}
|