@bcdflow/dev-inspector 2.0.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.
- package/bin/cli.js +244 -0
- package/dist/client/dev-inspector.js +7065 -0
- package/dist/client/dev-inspector.js.map +7 -0
- package/dist/mcp/server.js +189 -0
- package/dist/mcp/server.js.map +7 -0
- package/dist/middleware/index.js +444 -0
- package/dist/middleware/index.js.map +7 -0
- package/package.json +42 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/middleware/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createRoutes: () => createRoutes,
|
|
34
|
+
devInspector: () => devInspector,
|
|
35
|
+
getLicenseStatus: () => getLicenseStatus,
|
|
36
|
+
inspectorEmitter: () => inspectorEmitter,
|
|
37
|
+
waitForLicense: () => waitForLicense
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/middleware/routes.ts
|
|
42
|
+
var import_fs = require("fs");
|
|
43
|
+
var import_path = require("path");
|
|
44
|
+
|
|
45
|
+
// src/middleware/emitter.ts
|
|
46
|
+
var import_events = require("events");
|
|
47
|
+
var inspectorEmitter = new import_events.EventEmitter();
|
|
48
|
+
inspectorEmitter.setMaxListeners(50);
|
|
49
|
+
|
|
50
|
+
// src/mcp/tools.ts
|
|
51
|
+
var MCP_TOOLS = [
|
|
52
|
+
// ===== Free Tier (11개) =====
|
|
53
|
+
{ name: "inspect_element", tier: "free", description: "Inspect a DOM element by CSS selector. Returns selector, classes, computed styles, bounding box, hierarchy, accessibility, and text content.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" } }, required: ["selector"] } },
|
|
54
|
+
{ name: "select_element", tier: "free", description: "Visually select and highlight a DOM element on the page with annotation metadata.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, note: { type: "string", description: "Comment or note" }, intent: { type: "string", enum: ["fix", "change", "question", "approve"], description: "What action is needed" }, severity: { type: "string", enum: ["blocking", "important", "suggestion"], description: "How critical" } }, required: ["selector"] } },
|
|
55
|
+
{ name: "list_selections", tier: "free", description: "List all selected/annotated elements.", inputSchema: { type: "object", properties: {} } },
|
|
56
|
+
{ name: "get_computed_styles", tier: "free", description: "Get computed CSS styles of a DOM element.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, properties: { type: "array", items: { type: "string" }, description: "CSS properties to query" } }, required: ["selector"] } },
|
|
57
|
+
{ name: "clear_selections", tier: "free", description: "Clear all selections.", inputSchema: { type: "object", properties: {} } },
|
|
58
|
+
{ name: "get_pending", tier: "free", description: "Get all pending annotations.", inputSchema: { type: "object", properties: { sessionId: { type: "string" } } } },
|
|
59
|
+
{ name: "acknowledge", tier: "free", description: "Mark annotation as seen.", inputSchema: { type: "object", properties: { annotationId: { type: "string" } }, required: ["annotationId"] } },
|
|
60
|
+
{ name: "resolve", tier: "free", description: "Mark annotation as resolved.", inputSchema: { type: "object", properties: { annotationId: { type: "string" }, summary: { type: "string" } }, required: ["annotationId"] } },
|
|
61
|
+
{ name: "dismiss", tier: "free", description: "Dismiss annotation with reason.", inputSchema: { type: "object", properties: { annotationId: { type: "string" }, reason: { type: "string" } }, required: ["annotationId", "reason"] } },
|
|
62
|
+
{ name: "reply", tier: "free", description: "Add threaded reply to annotation.", inputSchema: { type: "object", properties: { annotationId: { type: "string" }, message: { type: "string" } }, required: ["annotationId", "message"] } },
|
|
63
|
+
{ name: "get_bug_context", tier: "free", description: "Get environment context: browser, viewport, URL, console errors, page uptime.", inputSchema: { type: "object", properties: {} } },
|
|
64
|
+
// ===== Pro Tier =====
|
|
65
|
+
{ name: "annotate_text", tier: "pro", description: "Find and highlight text on the page with optional note.", inputSchema: { type: "object", properties: { pattern: { type: "string", description: "Text to find" }, note: { type: "string", description: "Note" } }, required: ["pattern"] } },
|
|
66
|
+
{ name: "pause_animations", tier: "pro", description: "Pause or resume all animations.", inputSchema: { type: "object", properties: { paused: { type: "boolean", description: "true=pause, false=resume" } }, required: ["paused"] } },
|
|
67
|
+
{ name: "watch_annotations", tier: "pro", description: "Wait for new annotations (batched).", inputSchema: { type: "object", properties: { batchWindowSeconds: { type: "number" }, timeoutSeconds: { type: "number" } } } },
|
|
68
|
+
// React tools
|
|
69
|
+
{ name: "inspect_component", tier: "pro", description: "Inspect a React component. Returns component name, props, hierarchy, and source file hint.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector of element" }, name: { type: "string", description: "React component name" } } } },
|
|
70
|
+
{ name: "find_components", tier: "pro", description: "Scan the page for all React components with instance count and example selectors.", inputSchema: { type: "object", properties: { root: { type: "string", description: "CSS selector for root (defaults to body)" } } } },
|
|
71
|
+
// Dev Loop tools
|
|
72
|
+
{ name: "capture_page", tier: "pro", description: "Capture a screenshot of the page or a specific element. Returns base64 data URL.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector (omit for viewport)" }, fullPage: { type: "boolean", description: "Capture full viewport" } } } },
|
|
73
|
+
{ name: "extract_page_structure", tier: "pro", description: "Extract page structure: headings, images, buttons, links, form inputs, layout containers, colors, and fonts.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Root selector (defaults to body)" }, maxDepth: { type: "number", description: "Max DOM depth (default 5, max 10)" } } } },
|
|
74
|
+
{ name: "verify_element", tier: "pro", description: "Verify element properties against expected values (width, height, color, display, text, visible, position).", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, expected: { type: "object", description: "Expected values" } }, required: ["selector", "expected"] } },
|
|
75
|
+
{ name: "check_responsive", tier: "pro", description: "Check how an element is affected by media queries across breakpoints.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, breakpoints: { type: "array", items: { type: "number" }, description: "Viewport widths (default: [375, 768, 1024, 1440])" } }, required: ["selector"] } },
|
|
76
|
+
// Tier 1: Accessibility & Advanced
|
|
77
|
+
{ name: "audit_accessibility", tier: "pro", description: "Run accessibility audit: alt text, form labels, contrast, heading hierarchy, landmarks, ARIA issues.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector (defaults to page)" }, rules: { type: "array", items: { type: "string" }, description: "Specific rules to check" } } } },
|
|
78
|
+
{ name: "check_contrast", tier: "pro", description: "Check WCAG color contrast ratios for text elements.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Element selector" }, selectors: { type: "array", items: { type: "string" }, description: "Multiple selectors" } } } },
|
|
79
|
+
{ name: "inspect_css_rules", tier: "pro", description: "Inspect actual CSS rules (not computed) that apply to an element with specificity and source.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, pseudo: { type: "string", description: "Pseudo-element (e.g. ::before)" } }, required: ["selector"] } },
|
|
80
|
+
{ name: "capture_labeled", tier: "pro", description: "Capture screenshot with numbered labels on interactive elements.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector" }, labelTypes: { type: "array", items: { type: "string" }, description: "Element types to label" } } } },
|
|
81
|
+
// Tier 2: Design & Comparison
|
|
82
|
+
{ name: "extract_design_tokens", tier: "pro", description: "Extract design system tokens: colors, spacing, typography, borders, shadows.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector (defaults to body)" } } } },
|
|
83
|
+
{ name: "compare_screenshots", tier: "pro", description: "Compare two screenshots and report visual differences (10x10 grid analysis).", inputSchema: { type: "object", properties: { before: { type: "string", description: "Base64 data URL before" }, after: { type: "string", description: "Base64 data URL after" } }, required: ["before", "after"] } },
|
|
84
|
+
{ name: "inspect_react_state", tier: "pro", description: "Inspect React component hooks: useState, useReducer, useContext, useRef, useMemo values.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector of component element" } }, required: ["selector"] } },
|
|
85
|
+
{ name: "validate_page_structure", tier: "pro", description: "Validate page structure: headings, landmarks, images, links, forms, ARIA. Returns score 0-100.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector (defaults to page)" } } } },
|
|
86
|
+
// Tier 3: Code Generation & Metrics
|
|
87
|
+
{ name: "map_to_tailwind", tier: "pro", description: "Map element computed styles to equivalent Tailwind CSS classes.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" } }, required: ["selector"] } },
|
|
88
|
+
{ name: "get_performance_metrics", tier: "pro", description: "Get page performance: load timing, FP, FCP, LCP, CLS, DOM count, memory.", inputSchema: { type: "object", properties: {} } },
|
|
89
|
+
{ name: "generate_test_code", tier: "pro", description: "Generate Playwright or Cypress test code for a DOM element.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, framework: { type: "string", enum: ["playwright", "cypress"], description: "Test framework (default: playwright)" }, assertions: { type: "array", items: { type: "string" }, description: "Assertion types" } }, required: ["selector"] } },
|
|
90
|
+
{ name: "inspect_tab_order", tier: "pro", description: "Inspect sequential tab/focus order with accessibility warnings.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector" } } } },
|
|
91
|
+
// Export
|
|
92
|
+
{ name: "list_assets", tier: "pro", description: "List all images, SVGs, and CSS background images with src, alt, dimensions.", inputSchema: { type: "object", properties: {} } },
|
|
93
|
+
{ name: "export_bug_report", tier: "pro", description: "Export annotations as formatted bug report (github, linear, markdown, bug-report).", inputSchema: { type: "object", properties: { format: { type: "string", enum: ["github", "linear", "markdown", "bug-report"], description: "Output format (default: markdown)" } } } }
|
|
94
|
+
];
|
|
95
|
+
var FREE_TOOL_NAMES = new Set(
|
|
96
|
+
MCP_TOOLS.filter((t) => t.tier === "free").map((t) => t.name)
|
|
97
|
+
);
|
|
98
|
+
function getToolsForTier(tier) {
|
|
99
|
+
const tools = tier === "pro" ? MCP_TOOLS : MCP_TOOLS.filter((t) => t.tier === "free");
|
|
100
|
+
return tools.map(({ tier: _tier, ...rest }) => rest);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/license/validator.ts
|
|
104
|
+
var crypto = __toESM(require("crypto"));
|
|
105
|
+
var os = __toESM(require("os"));
|
|
106
|
+
var fs = __toESM(require("fs"));
|
|
107
|
+
var path = __toESM(require("path"));
|
|
108
|
+
var VALIDATE_URL = "https://gate.bcdflow.net/api/v1/license/validate";
|
|
109
|
+
var CACHE_DIR = path.join(os.homedir(), ".dev-inspector");
|
|
110
|
+
var CACHE_FILE = path.join(CACHE_DIR, "license-cache.json");
|
|
111
|
+
var CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
112
|
+
var VALIDATE_TIMEOUT = 5e3;
|
|
113
|
+
var FREE_STATUS = {
|
|
114
|
+
tier: "free",
|
|
115
|
+
valid: false,
|
|
116
|
+
expiresAt: null,
|
|
117
|
+
checkedAt: Date.now()
|
|
118
|
+
};
|
|
119
|
+
var _currentStatus = { ...FREE_STATUS };
|
|
120
|
+
function getMachineId() {
|
|
121
|
+
try {
|
|
122
|
+
const raw = os.hostname() + ":" + os.userInfo().username;
|
|
123
|
+
return crypto.createHash("sha256").update(raw).digest("hex").slice(0, 16);
|
|
124
|
+
} catch {
|
|
125
|
+
return "unknown";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function readCache(key) {
|
|
129
|
+
try {
|
|
130
|
+
if (!fs.existsSync(CACHE_FILE)) return null;
|
|
131
|
+
const data = JSON.parse(fs.readFileSync(CACHE_FILE, "utf-8"));
|
|
132
|
+
const keyHash = crypto.createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
133
|
+
const entry = data[keyHash];
|
|
134
|
+
if (!entry) return null;
|
|
135
|
+
if (Date.now() - entry.checkedAt > CACHE_TTL) return null;
|
|
136
|
+
return entry;
|
|
137
|
+
} catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function writeCache(key, status) {
|
|
142
|
+
try {
|
|
143
|
+
if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
144
|
+
let data = {};
|
|
145
|
+
try {
|
|
146
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
147
|
+
data = JSON.parse(fs.readFileSync(CACHE_FILE, "utf-8"));
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
const keyHash = crypto.createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
152
|
+
data[keyHash] = status;
|
|
153
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function remoteValidate(key) {
|
|
158
|
+
const body = JSON.stringify({
|
|
159
|
+
key,
|
|
160
|
+
package: "dev-inspector",
|
|
161
|
+
version: "2.0.0",
|
|
162
|
+
machineId: getMachineId()
|
|
163
|
+
});
|
|
164
|
+
const controller = new AbortController();
|
|
165
|
+
const timer = setTimeout(() => controller.abort(), VALIDATE_TIMEOUT);
|
|
166
|
+
try {
|
|
167
|
+
const res = await fetch(VALIDATE_URL, {
|
|
168
|
+
method: "POST",
|
|
169
|
+
headers: { "Content-Type": "application/json" },
|
|
170
|
+
body,
|
|
171
|
+
signal: controller.signal
|
|
172
|
+
});
|
|
173
|
+
clearTimeout(timer);
|
|
174
|
+
if (!res.ok) {
|
|
175
|
+
return { ...FREE_STATUS, checkedAt: Date.now() };
|
|
176
|
+
}
|
|
177
|
+
const data = await res.json();
|
|
178
|
+
return {
|
|
179
|
+
tier: data.tier === "pro" ? "pro" : "free",
|
|
180
|
+
valid: !!data.valid,
|
|
181
|
+
expiresAt: data.expiresAt || null,
|
|
182
|
+
checkedAt: Date.now()
|
|
183
|
+
};
|
|
184
|
+
} catch {
|
|
185
|
+
clearTimeout(timer);
|
|
186
|
+
return { ...FREE_STATUS, checkedAt: Date.now() };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function validateLicense(key) {
|
|
190
|
+
if (!key) {
|
|
191
|
+
_currentStatus = { ...FREE_STATUS, checkedAt: Date.now() };
|
|
192
|
+
return _currentStatus;
|
|
193
|
+
}
|
|
194
|
+
const cached = readCache(key);
|
|
195
|
+
if (cached) {
|
|
196
|
+
_currentStatus = cached;
|
|
197
|
+
return _currentStatus;
|
|
198
|
+
}
|
|
199
|
+
const result = await remoteValidate(key);
|
|
200
|
+
if (result.valid) {
|
|
201
|
+
writeCache(key, result);
|
|
202
|
+
_currentStatus = result;
|
|
203
|
+
return _currentStatus;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
207
|
+
const data = JSON.parse(fs.readFileSync(CACHE_FILE, "utf-8"));
|
|
208
|
+
const keyHash = crypto.createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
209
|
+
const expired = data[keyHash];
|
|
210
|
+
if (expired == null ? void 0 : expired.valid) {
|
|
211
|
+
_currentStatus = { ...expired, checkedAt: Date.now() };
|
|
212
|
+
return _currentStatus;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
_currentStatus = result;
|
|
218
|
+
return _currentStatus;
|
|
219
|
+
}
|
|
220
|
+
function getLicenseStatus() {
|
|
221
|
+
return _currentStatus;
|
|
222
|
+
}
|
|
223
|
+
function loadKeyFromConfig() {
|
|
224
|
+
try {
|
|
225
|
+
const configFile = path.join(CACHE_DIR, "config.json");
|
|
226
|
+
if (!fs.existsSync(configFile)) return void 0;
|
|
227
|
+
const config = JSON.parse(fs.readFileSync(configFile, "utf-8"));
|
|
228
|
+
return config.licenseKey || void 0;
|
|
229
|
+
} catch {
|
|
230
|
+
return void 0;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/middleware/routes.ts
|
|
235
|
+
function getExpress() {
|
|
236
|
+
try {
|
|
237
|
+
const { createRequire } = require("module");
|
|
238
|
+
const hostRequire = createRequire(process.cwd() + "/package.json");
|
|
239
|
+
return hostRequire("express");
|
|
240
|
+
} catch {
|
|
241
|
+
return require("express");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function createRoutes() {
|
|
245
|
+
const express = getExpress();
|
|
246
|
+
const router = express.Router();
|
|
247
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
248
|
+
const pendingResults = /* @__PURE__ */ new Map();
|
|
249
|
+
router.get("/stream", (req, res) => {
|
|
250
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
251
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
252
|
+
res.setHeader("Connection", "keep-alive");
|
|
253
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
254
|
+
res.flushHeaders();
|
|
255
|
+
res.write(`data: ${JSON.stringify({ type: "connected", timestamp: Date.now() })}
|
|
256
|
+
|
|
257
|
+
`);
|
|
258
|
+
sseClients.add(res);
|
|
259
|
+
const heartbeat = setInterval(() => {
|
|
260
|
+
res.write(": heartbeat\n\n");
|
|
261
|
+
}, 3e4);
|
|
262
|
+
req.on("close", () => {
|
|
263
|
+
clearInterval(heartbeat);
|
|
264
|
+
sseClients.delete(res);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
function broadcast(event, data) {
|
|
268
|
+
const payload = `event: ${event}
|
|
269
|
+
data: ${JSON.stringify(data)}
|
|
270
|
+
|
|
271
|
+
`;
|
|
272
|
+
for (const client of sseClients) {
|
|
273
|
+
try {
|
|
274
|
+
client.write(payload);
|
|
275
|
+
} catch {
|
|
276
|
+
sseClients.delete(client);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
router.post("/call", async (req, res) => {
|
|
281
|
+
const { id, tool, params } = req.body;
|
|
282
|
+
if (!id || !tool) {
|
|
283
|
+
res.status(400).json({ error: "id and tool are required" });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
await Promise.race([
|
|
288
|
+
waitForLicense(),
|
|
289
|
+
new Promise((r) => setTimeout(r, 2e3))
|
|
290
|
+
]);
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
const status = getLicenseStatus();
|
|
294
|
+
if (status.tier === "free" && !FREE_TOOL_NAMES.has(tool)) {
|
|
295
|
+
res.json({
|
|
296
|
+
result: {
|
|
297
|
+
error: `"${tool}" requires a Pro license. Set DEV_INSPECTOR_KEY for full access.`,
|
|
298
|
+
upgrade: true,
|
|
299
|
+
freeTier: true
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (sseClients.size === 0) {
|
|
305
|
+
res.json({ error: "No browser connected. Open a page with dev-inspector client loaded." });
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
broadcast("tool_call", { id, tool, params });
|
|
309
|
+
try {
|
|
310
|
+
const result = await new Promise((resolve, reject) => {
|
|
311
|
+
const timer = setTimeout(() => {
|
|
312
|
+
pendingResults.delete(id);
|
|
313
|
+
reject(new Error("Tool call timed out (30s)"));
|
|
314
|
+
}, 3e4);
|
|
315
|
+
pendingResults.set(id, { resolve, timer });
|
|
316
|
+
});
|
|
317
|
+
res.json({ result });
|
|
318
|
+
} catch (err) {
|
|
319
|
+
res.json({ error: err.message });
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
router.post("/result", (req, res) => {
|
|
323
|
+
const { id, result } = req.body;
|
|
324
|
+
if (!id) {
|
|
325
|
+
res.status(400).json({ error: "id is required" });
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const pending = pendingResults.get(id);
|
|
329
|
+
if (pending) {
|
|
330
|
+
clearTimeout(pending.timer);
|
|
331
|
+
pendingResults.delete(id);
|
|
332
|
+
pending.resolve(result);
|
|
333
|
+
}
|
|
334
|
+
res.json({ success: true });
|
|
335
|
+
});
|
|
336
|
+
router.post("/event", (req, res) => {
|
|
337
|
+
inspectorEmitter.emit("browser_event", req.body);
|
|
338
|
+
res.json({ success: true });
|
|
339
|
+
});
|
|
340
|
+
router.get("/tools", (_req, res) => {
|
|
341
|
+
const status = getLicenseStatus();
|
|
342
|
+
res.json({ tools: getToolsForTier(status.tier) });
|
|
343
|
+
});
|
|
344
|
+
router.get("/license-status", (_req, res) => {
|
|
345
|
+
const status = getLicenseStatus();
|
|
346
|
+
res.json({
|
|
347
|
+
tier: status.tier,
|
|
348
|
+
valid: status.valid,
|
|
349
|
+
expiresAt: status.expiresAt
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
let clientBundle = null;
|
|
353
|
+
router.get("/client.js", (_req, res) => {
|
|
354
|
+
if (!clientBundle) {
|
|
355
|
+
try {
|
|
356
|
+
clientBundle = (0, import_fs.readFileSync)((0, import_path.join)(__dirname, "..", "client", "dev-inspector.js"), "utf-8");
|
|
357
|
+
} catch {
|
|
358
|
+
res.status(404).send("// Client bundle not found. Run: npm run build");
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
363
|
+
res.send(clientBundle);
|
|
364
|
+
});
|
|
365
|
+
router.get("/status", (_req, res) => {
|
|
366
|
+
const status = getLicenseStatus();
|
|
367
|
+
res.json({
|
|
368
|
+
connected: sseClients.size > 0,
|
|
369
|
+
clients: sseClients.size,
|
|
370
|
+
pendingCalls: pendingResults.size,
|
|
371
|
+
license: status.tier
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
return router;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/middleware/index.ts
|
|
378
|
+
function getExpress2() {
|
|
379
|
+
try {
|
|
380
|
+
const { createRequire } = require("module");
|
|
381
|
+
const hostRequire = createRequire(process.cwd() + "/package.json");
|
|
382
|
+
return hostRequire("express");
|
|
383
|
+
} catch {
|
|
384
|
+
return require("express");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
var _licensePromise = null;
|
|
388
|
+
function devInspector(options = {}) {
|
|
389
|
+
const {
|
|
390
|
+
prefix = "/api/v1/dev-inspector",
|
|
391
|
+
enabled,
|
|
392
|
+
autoInject = false
|
|
393
|
+
} = options;
|
|
394
|
+
const express = getExpress2();
|
|
395
|
+
const router = express.Router();
|
|
396
|
+
const isEnabled = enabled !== void 0 ? enabled : process.env.NODE_ENV !== "production";
|
|
397
|
+
if (!isEnabled) {
|
|
398
|
+
return router;
|
|
399
|
+
}
|
|
400
|
+
const licenseKey = options.licenseKey || process.env.DEV_INSPECTOR_KEY || loadKeyFromConfig();
|
|
401
|
+
_licensePromise = validateLicense(licenseKey).then((status) => {
|
|
402
|
+
if (status.tier === "pro") {
|
|
403
|
+
console.log(`[dev-inspector] License: Pro (valid${status.expiresAt ? ` until ${status.expiresAt.split("T")[0]}` : ""})`);
|
|
404
|
+
} else if (licenseKey) {
|
|
405
|
+
console.log("[dev-inspector] License: invalid or expired \u2014 Free tier (11 tools)");
|
|
406
|
+
} else {
|
|
407
|
+
console.log("[dev-inspector] Free tier (11 tools). Set DEV_INSPECTOR_KEY for full access.");
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
router.use(prefix, createRoutes());
|
|
411
|
+
if (autoInject) {
|
|
412
|
+
router.use((req, res, next) => {
|
|
413
|
+
const originalSend = res.send.bind(res);
|
|
414
|
+
res.send = function(body) {
|
|
415
|
+
if (typeof body === "string" && (body.includes("</body>") || body.includes("</html>"))) {
|
|
416
|
+
const scriptTag = `<script src="${prefix}/client.js" defer></script>`;
|
|
417
|
+
if (body.includes("</body>")) {
|
|
418
|
+
body = body.replace("</body>", `${scriptTag}
|
|
419
|
+
</body>`);
|
|
420
|
+
} else if (body.includes("</html>")) {
|
|
421
|
+
body = body.replace("</html>", `${scriptTag}
|
|
422
|
+
</html>`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return originalSend(body);
|
|
426
|
+
};
|
|
427
|
+
next();
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return router;
|
|
431
|
+
}
|
|
432
|
+
function waitForLicense() {
|
|
433
|
+
if (_licensePromise) return _licensePromise;
|
|
434
|
+
return Promise.resolve();
|
|
435
|
+
}
|
|
436
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
437
|
+
0 && (module.exports = {
|
|
438
|
+
createRoutes,
|
|
439
|
+
devInspector,
|
|
440
|
+
getLicenseStatus,
|
|
441
|
+
inspectorEmitter,
|
|
442
|
+
waitForLicense
|
|
443
|
+
});
|
|
444
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/middleware/index.ts", "../../src/middleware/routes.ts", "../../src/middleware/emitter.ts", "../../src/mcp/tools.ts", "../../src/license/validator.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Dev InSpector Express Middleware\n *\n * Usage:\n * const { devInspector } = require('@bcdflow/dev-inspector');\n * app.use(devInspector({ licenseKey: process.env.DEV_INSPECTOR_KEY }));\n */\nimport { createRoutes } from './routes';\nimport { validateLicense, getLicenseStatus, loadKeyFromConfig } from '../license/validator';\n\nexport interface DevInspectorOptions {\n /** \uB77C\uC774\uC120\uC2A4 \uD0A4 (\uC5C6\uC73C\uBA74 DEV_INSPECTOR_KEY \uD658\uACBD\uBCC0\uC218 \uB610\uB294 ~/.dev-inspector/config.json) */\n licenseKey?: string;\n /** API \uB77C\uC6B0\uD2B8 \uC811\uB450\uC0AC (\uAE30\uBCF8: '/api/v1/dev-inspector') */\n prefix?: string;\n /** production\uC5D0\uC11C\uB3C4 \uD65C\uC131\uD654 (\uAE30\uBCF8: false) */\n enabled?: boolean;\n /** HTML \uC751\uB2F5\uC5D0 \uD074\uB77C\uC774\uC5B8\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC790\uB3D9 \uC8FC\uC785 (\uAE30\uBCF8: false) */\n autoInject?: boolean;\n}\n\n// express\uB97C \uD638\uC2A4\uD2B8 \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C resolve\nfunction getExpress(): any {\n try {\n const { createRequire } = require('module');\n const hostRequire = createRequire(process.cwd() + '/package.json');\n return hostRequire('express');\n } catch {\n return require('express');\n }\n}\n\n// \uB77C\uC774\uC120\uC2A4 \uAC80\uC99D Promise (startup \uC2DC \uD55C \uBC88\uB9CC)\nlet _licensePromise: Promise<void> | null = null;\n\nexport function devInspector(options: DevInspectorOptions = {}): any {\n const {\n prefix = '/api/v1/dev-inspector',\n enabled,\n autoInject = false,\n } = options;\n\n const express = getExpress();\n const router = express.Router();\n\n // production \uD658\uACBD \uCCB4\uD06C\n const isEnabled = enabled !== undefined ? enabled : process.env.NODE_ENV !== 'production';\n if (!isEnabled) {\n return router;\n }\n\n // \uB77C\uC774\uC120\uC2A4 \uD0A4 resolve: options > env > config file\n const licenseKey = options.licenseKey\n || process.env.DEV_INSPECTOR_KEY\n || loadKeyFromConfig();\n\n // \uBE44\uB3D9\uAE30 \uAC80\uC99D \uC2DC\uC791 (\uC11C\uBC84 \uC2DC\uC791\uC744 \uBE14\uB85D\uD558\uC9C0 \uC54A\uC74C)\n _licensePromise = validateLicense(licenseKey).then((status) => {\n if (status.tier === 'pro') {\n console.log(`[dev-inspector] License: Pro (valid${status.expiresAt ? ` until ${status.expiresAt.split('T')[0]}` : ''})`);\n } else if (licenseKey) {\n console.log('[dev-inspector] License: invalid or expired \u2014 Free tier (11 tools)');\n } else {\n console.log('[dev-inspector] Free tier (11 tools). Set DEV_INSPECTOR_KEY for full access.');\n }\n });\n\n // API \uB77C\uC6B0\uD2B8 \uB9C8\uC6B4\uD2B8\n router.use(prefix, createRoutes());\n\n // \uD074\uB77C\uC774\uC5B8\uD2B8 \uC790\uB3D9 \uC8FC\uC785\n if (autoInject) {\n router.use((req: any, res: any, next: any) => {\n const originalSend = res.send.bind(res);\n res.send = function (body: any) {\n if (typeof body === 'string' && (body.includes('</body>') || body.includes('</html>'))) {\n const scriptTag = `<script src=\"${prefix}/client.js\" defer><\\/script>`;\n if (body.includes('</body>')) {\n body = body.replace('</body>', `${scriptTag}\\n</body>`);\n } else if (body.includes('</html>')) {\n body = body.replace('</html>', `${scriptTag}\\n</html>`);\n }\n }\n return originalSend(body);\n };\n next();\n });\n }\n\n return router;\n}\n\n/** \uB77C\uC774\uC120\uC2A4 \uAC80\uC99D \uC644\uB8CC \uB300\uAE30 (routes\uC5D0\uC11C \uC0AC\uC6A9) */\nexport function waitForLicense(): Promise<void> {\n if (_licensePromise) return _licensePromise;\n return Promise.resolve();\n}\n\nexport { createRoutes } from './routes';\nexport { inspectorEmitter } from './emitter';\nexport { getLicenseStatus } from '../license/validator';\n", "/**\n * Dev Inspector MCP Routes \u2014 SSE + REST \uBE0C\uB9BF\uC9C0 + \uB77C\uC774\uC120\uC2A4 \uAC8C\uC774\uD305\n */\nimport { readFileSync } from 'fs';\nimport { join } from 'path';\nimport { inspectorEmitter } from './emitter';\nimport { getToolsForTier, FREE_TOOL_NAMES } from '../mcp/tools';\nimport { getLicenseStatus } from '../license/validator';\nimport { waitForLicense } from './index';\n\nfunction getExpress(): any {\n try {\n const { createRequire } = require('module');\n const hostRequire = createRequire(process.cwd() + '/package.json');\n return hostRequire('express');\n } catch {\n return require('express');\n }\n}\n\nexport function createRoutes(): any {\n const express = getExpress();\n const router = express.Router();\n\n const sseClients = new Set<any>();\n const pendingResults = new Map<string, {\n resolve: (value: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n }>();\n\n // ========== SSE \uC2A4\uD2B8\uB9BC ==========\n router.get('/stream', (req: any, res: any) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n res.write(`data: ${JSON.stringify({ type: 'connected', timestamp: Date.now() })}\\n\\n`);\n sseClients.add(res);\n\n const heartbeat = setInterval(() => {\n res.write(': heartbeat\\n\\n');\n }, 30000);\n\n req.on('close', () => {\n clearInterval(heartbeat);\n sseClients.delete(res);\n });\n });\n\n function broadcast(event: string, data: unknown): void {\n const payload = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n for (const client of sseClients) {\n try { client.write(payload); } catch { sseClients.delete(client); }\n }\n }\n\n // ========== \uB3C4\uAD6C \uD638\uCD9C (\uB77C\uC774\uC120\uC2A4 \uAC8C\uC774\uD305) ==========\n router.post('/call', async (req: any, res: any) => {\n const { id, tool, params } = req.body;\n if (!id || !tool) {\n res.status(400).json({ error: 'id and tool are required' });\n return;\n }\n\n // \uB77C\uC774\uC120\uC2A4 \uAC80\uC99D \uC644\uB8CC \uB300\uAE30 (\uCD5C\uB300 2\uCD08)\n try {\n await Promise.race([\n waitForLicense(),\n new Promise(r => setTimeout(r, 2000)),\n ]);\n } catch { /* ignore */ }\n\n // Pro \uB3C4\uAD6C\uB97C Free tier\uC5D0\uC11C \uD638\uCD9C \uC2DC \uCC28\uB2E8\n const status = getLicenseStatus();\n if (status.tier === 'free' && !FREE_TOOL_NAMES.has(tool)) {\n res.json({\n result: {\n error: `\"${tool}\" requires a Pro license. Set DEV_INSPECTOR_KEY for full access.`,\n upgrade: true,\n freeTier: true,\n },\n });\n return;\n }\n\n if (sseClients.size === 0) {\n res.json({ error: 'No browser connected. Open a page with dev-inspector client loaded.' });\n return;\n }\n\n broadcast('tool_call', { id, tool, params });\n\n try {\n const result = await new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingResults.delete(id);\n reject(new Error('Tool call timed out (30s)'));\n }, 30000);\n pendingResults.set(id, { resolve, timer });\n });\n res.json({ result });\n } catch (err: any) {\n res.json({ error: err.message });\n }\n });\n\n // ========== \uB3C4\uAD6C \uACB0\uACFC ==========\n router.post('/result', (req: any, res: any) => {\n const { id, result } = req.body;\n if (!id) {\n res.status(400).json({ error: 'id is required' });\n return;\n }\n const pending = pendingResults.get(id);\n if (pending) {\n clearTimeout(pending.timer);\n pendingResults.delete(id);\n pending.resolve(result);\n }\n res.json({ success: true });\n });\n\n // ========== \uBE0C\uB77C\uC6B0\uC800 \uC774\uBCA4\uD2B8 ==========\n router.post('/event', (req: any, res: any) => {\n inspectorEmitter.emit('browser_event', req.body);\n res.json({ success: true });\n });\n\n // ========== \uB3C4\uAD6C \uBAA9\uB85D (\uB77C\uC774\uC120\uC2A4 \uAE30\uBC18 \uD544\uD130\uB9C1) ==========\n router.get('/tools', (_req: any, res: any) => {\n const status = getLicenseStatus();\n res.json({ tools: getToolsForTier(status.tier) });\n });\n\n // ========== \uB77C\uC774\uC120\uC2A4 \uC0C1\uD0DC ==========\n router.get('/license-status', (_req: any, res: any) => {\n const status = getLicenseStatus();\n res.json({\n tier: status.tier,\n valid: status.valid,\n expiresAt: status.expiresAt,\n });\n });\n\n // ========== \uD074\uB77C\uC774\uC5B8\uD2B8 JS \uBC88\uB4E4 ==========\n let clientBundle: string | null = null;\n\n router.get('/client.js', (_req: any, res: any) => {\n if (!clientBundle) {\n try {\n clientBundle = readFileSync(join(__dirname, '..', 'client', 'dev-inspector.js'), 'utf-8');\n } catch {\n res.status(404).send('// Client bundle not found. Run: npm run build');\n return;\n }\n }\n res.setHeader('Content-Type', 'application/javascript');\n res.send(clientBundle);\n });\n\n // ========== \uC0C1\uD0DC \uD655\uC778 ==========\n router.get('/status', (_req: any, res: any) => {\n const status = getLicenseStatus();\n res.json({\n connected: sseClients.size > 0,\n clients: sseClients.size,\n pendingCalls: pendingResults.size,\n license: status.tier,\n });\n });\n\n return router;\n}\n", "/**\n * Inspector Event Emitter \u2014 \uB3C4\uAD6C \uD638\uCD9C/\uACB0\uACFC \uBE0C\uB9BF\uC9C0\n * MCP \uC11C\uBC84 \u2192 Express \uB77C\uC6B0\uD2B8 \u2192 SSE \u2192 \uBE0C\uB77C\uC6B0\uC800 \uAC04 \uC774\uBCA4\uD2B8 \uC911\uACC4\n */\nimport { EventEmitter } from 'events';\n\nexport const inspectorEmitter = new EventEmitter();\ninspectorEmitter.setMaxListeners(50);\n", "/**\n * MCP Tool Definitions \u2014 34\uAC1C DOM \uAC80\uC0AC \uB3C4\uAD6C\n * tier: 'free' (11\uAC1C) | 'pro' (23\uAC1C)\n */\nexport const MCP_TOOLS = [\n // ===== Free Tier (11\uAC1C) =====\n { name: 'inspect_element', tier: 'free', description: 'Inspect a DOM element by CSS selector. Returns selector, classes, computed styles, bounding box, hierarchy, accessibility, and text content.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' } }, required: ['selector'] } },\n { name: 'select_element', tier: 'free', description: 'Visually select and highlight a DOM element on the page with annotation metadata.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, note: { type: 'string', description: 'Comment or note' }, intent: { type: 'string', enum: ['fix', 'change', 'question', 'approve'], description: 'What action is needed' }, severity: { type: 'string', enum: ['blocking', 'important', 'suggestion'], description: 'How critical' } }, required: ['selector'] } },\n { name: 'list_selections', tier: 'free', description: 'List all selected/annotated elements.', inputSchema: { type: 'object', properties: {} } },\n { name: 'get_computed_styles', tier: 'free', description: 'Get computed CSS styles of a DOM element.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, properties: { type: 'array', items: { type: 'string' }, description: 'CSS properties to query' } }, required: ['selector'] } },\n { name: 'clear_selections', tier: 'free', description: 'Clear all selections.', inputSchema: { type: 'object', properties: {} } },\n { name: 'get_pending', tier: 'free', description: 'Get all pending annotations.', inputSchema: { type: 'object', properties: { sessionId: { type: 'string' } } } },\n { name: 'acknowledge', tier: 'free', description: 'Mark annotation as seen.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' } }, required: ['annotationId'] } },\n { name: 'resolve', tier: 'free', description: 'Mark annotation as resolved.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' }, summary: { type: 'string' } }, required: ['annotationId'] } },\n { name: 'dismiss', tier: 'free', description: 'Dismiss annotation with reason.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' }, reason: { type: 'string' } }, required: ['annotationId', 'reason'] } },\n { name: 'reply', tier: 'free', description: 'Add threaded reply to annotation.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' }, message: { type: 'string' } }, required: ['annotationId', 'message'] } },\n { name: 'get_bug_context', tier: 'free', description: 'Get environment context: browser, viewport, URL, console errors, page uptime.', inputSchema: { type: 'object', properties: {} } },\n\n // ===== Pro Tier =====\n { name: 'annotate_text', tier: 'pro', description: 'Find and highlight text on the page with optional note.', inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Text to find' }, note: { type: 'string', description: 'Note' } }, required: ['pattern'] } },\n { name: 'pause_animations', tier: 'pro', description: 'Pause or resume all animations.', inputSchema: { type: 'object', properties: { paused: { type: 'boolean', description: 'true=pause, false=resume' } }, required: ['paused'] } },\n { name: 'watch_annotations', tier: 'pro', description: 'Wait for new annotations (batched).', inputSchema: { type: 'object', properties: { batchWindowSeconds: { type: 'number' }, timeoutSeconds: { type: 'number' } } } },\n // React tools\n { name: 'inspect_component', tier: 'pro', description: 'Inspect a React component. Returns component name, props, hierarchy, and source file hint.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector of element' }, name: { type: 'string', description: 'React component name' } } } },\n { name: 'find_components', tier: 'pro', description: 'Scan the page for all React components with instance count and example selectors.', inputSchema: { type: 'object', properties: { root: { type: 'string', description: 'CSS selector for root (defaults to body)' } } } },\n // Dev Loop tools\n { name: 'capture_page', tier: 'pro', description: 'Capture a screenshot of the page or a specific element. Returns base64 data URL.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector (omit for viewport)' }, fullPage: { type: 'boolean', description: 'Capture full viewport' } } } },\n { name: 'extract_page_structure', tier: 'pro', description: 'Extract page structure: headings, images, buttons, links, form inputs, layout containers, colors, and fonts.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Root selector (defaults to body)' }, maxDepth: { type: 'number', description: 'Max DOM depth (default 5, max 10)' } } } },\n { name: 'verify_element', tier: 'pro', description: 'Verify element properties against expected values (width, height, color, display, text, visible, position).', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, expected: { type: 'object', description: 'Expected values' } }, required: ['selector', 'expected'] } },\n { name: 'check_responsive', tier: 'pro', description: 'Check how an element is affected by media queries across breakpoints.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, breakpoints: { type: 'array', items: { type: 'number' }, description: 'Viewport widths (default: [375, 768, 1024, 1440])' } }, required: ['selector'] } },\n // Tier 1: Accessibility & Advanced\n { name: 'audit_accessibility', tier: 'pro', description: 'Run accessibility audit: alt text, form labels, contrast, heading hierarchy, landmarks, ARIA issues.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector (defaults to page)' }, rules: { type: 'array', items: { type: 'string' }, description: 'Specific rules to check' } } } },\n { name: 'check_contrast', tier: 'pro', description: 'Check WCAG color contrast ratios for text elements.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Element selector' }, selectors: { type: 'array', items: { type: 'string' }, description: 'Multiple selectors' } } } },\n { name: 'inspect_css_rules', tier: 'pro', description: 'Inspect actual CSS rules (not computed) that apply to an element with specificity and source.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, pseudo: { type: 'string', description: 'Pseudo-element (e.g. ::before)' } }, required: ['selector'] } },\n { name: 'capture_labeled', tier: 'pro', description: 'Capture screenshot with numbered labels on interactive elements.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector' }, labelTypes: { type: 'array', items: { type: 'string' }, description: 'Element types to label' } } } },\n // Tier 2: Design & Comparison\n { name: 'extract_design_tokens', tier: 'pro', description: 'Extract design system tokens: colors, spacing, typography, borders, shadows.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector (defaults to body)' } } } },\n { name: 'compare_screenshots', tier: 'pro', description: 'Compare two screenshots and report visual differences (10x10 grid analysis).', inputSchema: { type: 'object', properties: { before: { type: 'string', description: 'Base64 data URL before' }, after: { type: 'string', description: 'Base64 data URL after' } }, required: ['before', 'after'] } },\n { name: 'inspect_react_state', tier: 'pro', description: 'Inspect React component hooks: useState, useReducer, useContext, useRef, useMemo values.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector of component element' } }, required: ['selector'] } },\n { name: 'validate_page_structure', tier: 'pro', description: 'Validate page structure: headings, landmarks, images, links, forms, ARIA. Returns score 0-100.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector (defaults to page)' } } } },\n // Tier 3: Code Generation & Metrics\n { name: 'map_to_tailwind', tier: 'pro', description: 'Map element computed styles to equivalent Tailwind CSS classes.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' } }, required: ['selector'] } },\n { name: 'get_performance_metrics', tier: 'pro', description: 'Get page performance: load timing, FP, FCP, LCP, CLS, DOM count, memory.', inputSchema: { type: 'object', properties: {} } },\n { name: 'generate_test_code', tier: 'pro', description: 'Generate Playwright or Cypress test code for a DOM element.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, framework: { type: 'string', enum: ['playwright', 'cypress'], description: 'Test framework (default: playwright)' }, assertions: { type: 'array', items: { type: 'string' }, description: 'Assertion types' } }, required: ['selector'] } },\n { name: 'inspect_tab_order', tier: 'pro', description: 'Inspect sequential tab/focus order with accessibility warnings.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector' } } } },\n // Export\n { name: 'list_assets', tier: 'pro', description: 'List all images, SVGs, and CSS background images with src, alt, dimensions.', inputSchema: { type: 'object', properties: {} } },\n { name: 'export_bug_report', tier: 'pro', description: 'Export annotations as formatted bug report (github, linear, markdown, bug-report).', inputSchema: { type: 'object', properties: { format: { type: 'string', enum: ['github', 'linear', 'markdown', 'bug-report'], description: 'Output format (default: markdown)' } } } },\n];\n\n/** Free tier \uB3C4\uAD6C \uC774\uB984 Set */\nexport const FREE_TOOL_NAMES = new Set(\n MCP_TOOLS.filter(t => t.tier === 'free').map(t => t.name)\n);\n\n/** MCP \uC751\uB2F5\uC6A9: tier \uD544\uB4DC \uC81C\uAC70 */\nexport function getToolsForTier(tier: 'free' | 'pro') {\n const tools = tier === 'pro' ? MCP_TOOLS : MCP_TOOLS.filter(t => t.tier === 'free');\n return tools.map(({ tier: _tier, ...rest }) => rest);\n}\n", "/**\n * License Validator \u2014 \uB77C\uC774\uC120\uC2A4 \uD0A4 \uAC80\uC99D + \uCE90\uC2DC\n * gate.bcdflow.net\uC73C\uB85C \uD0A4 \uAC80\uC99D, ~/.dev-inspector/license-cache.json\uC5D0 \uCE90\uC2DC\n */\nimport * as crypto from 'crypto';\nimport * as os from 'os';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst VALIDATE_URL = 'https://gate.bcdflow.net/api/v1/license/validate';\nconst CACHE_DIR = path.join(os.homedir(), '.dev-inspector');\nconst CACHE_FILE = path.join(CACHE_DIR, 'license-cache.json');\nconst CACHE_TTL = 24 * 60 * 60 * 1000; // 24\uC2DC\uAC04\nconst VALIDATE_TIMEOUT = 5000; // 5\uCD08\n\nexport interface LicenseStatus {\n tier: 'free' | 'pro';\n valid: boolean;\n expiresAt: string | null;\n checkedAt: number;\n}\n\nconst FREE_STATUS: LicenseStatus = {\n tier: 'free',\n valid: false,\n expiresAt: null,\n checkedAt: Date.now(),\n};\n\nlet _currentStatus: LicenseStatus = { ...FREE_STATUS };\n\n// ========== Machine ID ==========\nfunction getMachineId(): string {\n try {\n const raw = os.hostname() + ':' + os.userInfo().username;\n return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);\n } catch {\n return 'unknown';\n }\n}\n\n// ========== \uCE90\uC2DC ==========\nfunction readCache(key: string): LicenseStatus | null {\n try {\n if (!fs.existsSync(CACHE_FILE)) return null;\n const data = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));\n const keyHash = crypto.createHash('sha256').update(key).digest('hex').slice(0, 16);\n const entry = data[keyHash];\n if (!entry) return null;\n // TTL \uCCB4\uD06C\n if (Date.now() - entry.checkedAt > CACHE_TTL) return null;\n return entry as LicenseStatus;\n } catch {\n return null;\n }\n}\n\nfunction writeCache(key: string, status: LicenseStatus): void {\n try {\n if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });\n let data: Record<string, unknown> = {};\n try {\n if (fs.existsSync(CACHE_FILE)) {\n data = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));\n }\n } catch { /* ignore */ }\n const keyHash = crypto.createHash('sha256').update(key).digest('hex').slice(0, 16);\n data[keyHash] = status;\n fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));\n } catch { /* ignore */ }\n}\n\n// ========== \uC6D0\uACA9 \uAC80\uC99D ==========\nasync function remoteValidate(key: string): Promise<LicenseStatus> {\n const body = JSON.stringify({\n key,\n package: 'dev-inspector',\n version: '2.0.0',\n machineId: getMachineId(),\n });\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), VALIDATE_TIMEOUT);\n\n try {\n const res = await fetch(VALIDATE_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n signal: controller.signal,\n });\n clearTimeout(timer);\n\n if (!res.ok) {\n return { ...FREE_STATUS, checkedAt: Date.now() };\n }\n\n const data = await res.json();\n return {\n tier: data.tier === 'pro' ? 'pro' : 'free',\n valid: !!data.valid,\n expiresAt: data.expiresAt || null,\n checkedAt: Date.now(),\n };\n } catch {\n clearTimeout(timer);\n return { ...FREE_STATUS, checkedAt: Date.now() };\n }\n}\n\n// ========== \uACF5\uAC1C API ==========\n\n/**\n * \uB77C\uC774\uC120\uC2A4 \uD0A4 \uAC80\uC99D (\uBE44\uB3D9\uAE30)\n * 1. \uD0A4\uAC00 \uC5C6\uC73C\uBA74 \u2192 free\n * 2. \uCE90\uC2DC\uAC00 \uC720\uD6A8\uD558\uBA74 \u2192 \uCE90\uC2DC \uC0AC\uC6A9\n * 3. \uC6D0\uACA9 \uAC80\uC99D \u2192 \uACB0\uACFC \uCE90\uC2DC\n * 4. \uC6D0\uACA9 \uC2E4\uD328 \u2192 \uB9CC\uB8CC\uB41C \uCE90\uC2DC\uB77C\uB3C4 \uC0AC\uC6A9, \uC5C6\uC73C\uBA74 free\n */\nexport async function validateLicense(key?: string): Promise<LicenseStatus> {\n if (!key) {\n _currentStatus = { ...FREE_STATUS, checkedAt: Date.now() };\n return _currentStatus;\n }\n\n // \uCE90\uC2DC \uD655\uC778\n const cached = readCache(key);\n if (cached) {\n _currentStatus = cached;\n return _currentStatus;\n }\n\n // \uC6D0\uACA9 \uAC80\uC99D\n const result = await remoteValidate(key);\n\n if (result.valid) {\n writeCache(key, result);\n _currentStatus = result;\n return _currentStatus;\n }\n\n // \uC6D0\uACA9 \uC2E4\uD328 \uC2DC \uB9CC\uB8CC\uB41C \uCE90\uC2DC\uB77C\uB3C4 \uD655\uC778\n try {\n if (fs.existsSync(CACHE_FILE)) {\n const data = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));\n const keyHash = crypto.createHash('sha256').update(key).digest('hex').slice(0, 16);\n const expired = data[keyHash];\n if (expired?.valid) {\n // \uB9CC\uB8CC\uB41C \uCE90\uC2DC\uC9C0\uB9CC grace period \uD5C8\uC6A9 (\uCD94\uAC00 24\uC2DC\uAC04)\n _currentStatus = { ...expired, checkedAt: Date.now() };\n return _currentStatus;\n }\n }\n } catch { /* ignore */ }\n\n _currentStatus = result;\n return _currentStatus;\n}\n\n/** \uD604\uC7AC \uB77C\uC774\uC120\uC2A4 \uC0C1\uD0DC (\uB3D9\uAE30, validateLicense \uD638\uCD9C \uD6C4 \uC0AC\uC6A9) */\nexport function getLicenseStatus(): LicenseStatus {\n return _currentStatus;\n}\n\n/** \uD65C\uC131\uD654 \uCEE4\uB9E8\uB4DC\uC6A9: \uD0A4\uB97C config\uC5D0 \uC800\uC7A5 */\nexport function saveKeyToConfig(key: string): void {\n try {\n if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });\n const configFile = path.join(CACHE_DIR, 'config.json');\n let config: Record<string, unknown> = {};\n try {\n if (fs.existsSync(configFile)) {\n config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));\n }\n } catch { /* ignore */ }\n config.licenseKey = key;\n fs.writeFileSync(configFile, JSON.stringify(config, null, 2));\n } catch { /* ignore */ }\n}\n\n/** config\uC5D0\uC11C \uC800\uC7A5\uB41C \uD0A4 \uC77D\uAE30 */\nexport function loadKeyFromConfig(): string | undefined {\n try {\n const configFile = path.join(CACHE_DIR, 'config.json');\n if (!fs.existsSync(configFile)) return undefined;\n const config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));\n return config.licenseKey || undefined;\n } catch {\n return undefined;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["getExpress"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bcdflow/dev-inspector",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "DOM inspector + MCP bridge for AI coding assistants. Express middleware that enables Claude Code to inspect, annotate, and audit web pages in real-time.",
|
|
5
|
+
"main": "dist/middleware/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dev-inspector": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/middleware/index.js",
|
|
11
|
+
"./mcp": "./dist/mcp/server.js",
|
|
12
|
+
"./client": "./dist/client/dev-inspector.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "node esbuild.config.mjs",
|
|
16
|
+
"watch": "node esbuild.config.mjs --watch",
|
|
17
|
+
"clean": "rm -rf dist",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": ["mcp", "dom-inspector", "claude", "ai", "express-middleware", "accessibility", "devtools"],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"express": ">=4.0.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependenciesMeta": {
|
|
26
|
+
"express": {
|
|
27
|
+
"optional": false
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"esbuild": "^0.24.0",
|
|
32
|
+
"typescript": "^5.7.0"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist/",
|
|
36
|
+
"bin/",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=16.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|