@bcdflow/dev-inspector 2.0.0 → 2.0.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/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # @bcdflow/dev-inspector
2
+
3
+ AI 코딩 어시스턴트(Claude Code)와 연동하여 웹 페이지를 실시간으로 검사, 어노테이션, 감사할 수 있는 Express 미들웨어입니다.
4
+
5
+ ```
6
+ Claude Code → MCP Server (stdio) → Express API (SSE+REST) → Client JS (in-page) → DOM
7
+ ```
8
+
9
+ ## Quick Start
10
+
11
+ ### 1. 설치
12
+
13
+ ```bash
14
+ npm install -D @bcdflow/dev-inspector
15
+ ```
16
+
17
+ ### 2. Express 앱에 미들웨어 추가
18
+
19
+ ```javascript
20
+ const { devInspector } = require('@bcdflow/dev-inspector');
21
+
22
+ // 개발 환경에서만 활성화 (production에서 자동 비활성)
23
+ app.use(devInspector({ autoInject: true }));
24
+ ```
25
+
26
+ ### 3. MCP 서버 등록 (Claude Code 연동)
27
+
28
+ ```bash
29
+ npx dev-inspector setup
30
+ ```
31
+
32
+ ### 4. 사용
33
+
34
+ - 서버 시작 후 브라우저에서 페이지 열기
35
+ - 우하단 돋보기 버튼 클릭 또는 `Ctrl+Shift+X`로 인스펙터 토글
36
+ - Claude Code에서 MCP 도구로 DOM 검사, 어노테이션, 접근성 감사 등 사용
37
+
38
+ ---
39
+
40
+ ## License Key (Pro)
41
+
42
+ Free tier는 11개 기본 도구를 제공합니다. Pro 라이선스를 활성화하면 34개 전체 도구를 사용할 수 있습니다.
43
+
44
+ ### 활성화 방법
45
+
46
+ **방법 1: CLI로 활성화** (권장)
47
+
48
+ ```bash
49
+ npx dev-inspector activate DI-XXXX-XXXX-XXXX
50
+ ```
51
+
52
+ 키가 `~/.dev-inspector/config.json`에 저장되어 모든 프로젝트에서 자동 적용됩니다.
53
+
54
+ **방법 2: 환경변수**
55
+
56
+ ```bash
57
+ # .env
58
+ DEV_INSPECTOR_KEY=DI-XXXX-XXXX-XXXX
59
+ ```
60
+
61
+ **방법 3: 미들웨어 옵션**
62
+
63
+ ```javascript
64
+ app.use(devInspector({
65
+ licenseKey: process.env.DEV_INSPECTOR_KEY,
66
+ autoInject: true,
67
+ }));
68
+ ```
69
+
70
+ ### 상태 확인
71
+
72
+ ```bash
73
+ npx dev-inspector status
74
+ ```
75
+
76
+ ### Free vs Pro
77
+
78
+ | 기능 | Free | Pro |
79
+ |------|:----:|:---:|
80
+ | DOM 검사 (`inspect_element`) | O | O |
81
+ | 요소 선택/하이라이트 (`select_element`) | O | O |
82
+ | 어노테이션 관리 (acknowledge, resolve, dismiss, reply) | O | O |
83
+ | 버그 컨텍스트 (`get_bug_context`) | O | O |
84
+ | 텍스트 어노테이션 (`annotate_text`) | - | O |
85
+ | 애니메이션 제어 (`pause_animations`) | - | O |
86
+ | React 컴포넌트 검사 (`inspect_component`, `find_components`) | - | O |
87
+ | 스크린샷 캡처 (`capture_page`, `capture_labeled`) | - | O |
88
+ | 접근성 감사 (`audit_accessibility`, `check_contrast`) | - | O |
89
+ | CSS 규칙 검사 (`inspect_css_rules`) | - | O |
90
+ | 디자인 토큰 추출 (`extract_design_tokens`) | - | O |
91
+ | React 상태 검사 (`inspect_react_state`) | - | O |
92
+ | 페이지 구조 검증 (`validate_page_structure`) | - | O |
93
+ | Tailwind 매핑 (`map_to_tailwind`) | - | O |
94
+ | 성능 메트릭 (`get_performance_metrics`) | - | O |
95
+ | 테스트 코드 생성 (`generate_test_code`) | - | O |
96
+ | 탭 순서 검사 (`inspect_tab_order`) | - | O |
97
+ | 에셋 목록 (`list_assets`) | - | O |
98
+ | 버그 리포트 내보내기 (`export_bug_report`) | - | O |
99
+
100
+ ---
101
+
102
+ ## 미들웨어 옵션
103
+
104
+ ```javascript
105
+ app.use(devInspector({
106
+ // 라이선스 키 (없으면 DEV_INSPECTOR_KEY 환경변수 또는 ~/.dev-inspector/config.json)
107
+ licenseKey: 'DI-XXXX-XXXX-XXXX',
108
+
109
+ // API 라우트 접두사 (기본: '/api/v1/dev-inspector')
110
+ prefix: '/api/v1/dev-inspector',
111
+
112
+ // production에서도 활성화 (기본: false — production에서 자동 비활성)
113
+ enabled: false,
114
+
115
+ // HTML 응답에 클라이언트 스크립트 자동 주입 (기본: false)
116
+ autoInject: true,
117
+ }));
118
+ ```
119
+
120
+ ### autoInject 없이 수동으로 스크립트 추가
121
+
122
+ ```html
123
+ <script src="/api/v1/dev-inspector/client.js" defer></script>
124
+ ```
125
+
126
+ ---
127
+
128
+ ## CLI 명령어
129
+
130
+ ```bash
131
+ npx dev-inspector setup # Claude Code MCP 서버 등록
132
+ npx dev-inspector setup --global # 전역 등록 (모든 프로젝트)
133
+ npx dev-inspector activate <key> # Pro 라이선스 활성화
134
+ npx dev-inspector status # 라이선스 상태 확인
135
+ npx dev-inspector mcp # MCP 서버 직접 실행 (디버그)
136
+ ```
137
+
138
+ ---
139
+
140
+ ## API 엔드포인트
141
+
142
+ 미들웨어가 자동으로 마운트하는 엔드포인트:
143
+
144
+ | Method | Path | 설명 |
145
+ |--------|------|------|
146
+ | GET | `{prefix}/stream` | SSE 스트림 (브라우저 연결) |
147
+ | POST | `{prefix}/call` | 도구 호출 (MCP → 브라우저) |
148
+ | POST | `{prefix}/result` | 도구 결과 (브라우저 → MCP) |
149
+ | GET | `{prefix}/tools` | 사용 가능한 도구 목록 |
150
+ | GET | `{prefix}/license-status` | 라이선스 상태 |
151
+ | GET | `{prefix}/status` | 연결 상태 |
152
+ | GET | `{prefix}/client.js` | 클라이언트 번들 |
153
+
154
+ ---
155
+
156
+ ## 스크린샷 기능
157
+
158
+ 스크린샷 도구(`capture_page`, `capture_labeled`)를 사용하려면 html2canvas가 필요합니다:
159
+
160
+ ```html
161
+ <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
162
+ ```
163
+
164
+ ---
165
+
166
+ ## 아키텍처
167
+
168
+ ```
169
+ ┌─────────────┐ stdio ┌─────────────┐ HTTP ┌─────────────────┐
170
+ │ Claude Code │ ──────────────→ │ MCP Server │ ────────────→ │ Express 미들웨어 │
171
+ │ │ ←────────────── │ (server.js) │ ←──────────── │ (SSE + REST) │
172
+ └─────────────┘ JSON-RPC └─────────────┘ POST/GET └────────┬────────┘
173
+ │ SSE
174
+
175
+ ┌───────────────┐
176
+ │ Client JS │
177
+ │ (브라우저 내) │
178
+ │ → DOM 접근 │
179
+ └───────────────┘
180
+ ```
181
+
182
+ ---
183
+
184
+ ## 요구 사항
185
+
186
+ - Node.js >= 16
187
+ - Express >= 4.0
188
+ - Claude Code (MCP 연동 시)
189
+
190
+ ## License
191
+
192
+ MIT
package/bin/cli.js CHANGED
@@ -120,15 +120,10 @@ function activate() {
120
120
 
121
121
  // 즉시 검증 시도
122
122
  console.log('Validating license...');
123
- const body = JSON.stringify({
124
- key,
125
- package: 'dev-inspector',
126
- version: '2.0.0',
127
- machineId: getMachineId(),
128
- });
123
+ const body = JSON.stringify({ key });
129
124
 
130
125
  const https = require('https');
131
- const req = https.request('https://gate.bcdflow.net/api/v1/license/validate', {
126
+ const req = https.request('https://gate.bcdflow.net/api/license/validate', {
132
127
  method: 'POST',
133
128
  headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
134
129
  timeout: 5000,
@@ -145,16 +140,17 @@ function activate() {
145
140
  try {
146
141
  if (fs.existsSync(CACHE_FILE)) cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
147
142
  } catch {}
143
+ const expiresAt = result.license?.expiresAt || null;
148
144
  cache[keyHash] = {
149
- tier: result.tier || 'pro',
145
+ tier: 'pro',
150
146
  valid: true,
151
- expiresAt: result.expiresAt || null,
147
+ expiresAt,
152
148
  checkedAt: Date.now(),
153
149
  };
154
150
  fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
155
151
 
156
- console.log(`\n✓ License activated: ${result.tier || 'Pro'}`);
157
- if (result.expiresAt) console.log(` Expires: ${result.expiresAt.split('T')[0]}`);
152
+ console.log(`\n✓ License activated: Pro`);
153
+ if (expiresAt) console.log(` Expires: ${expiresAt.split('T')[0]}`);
158
154
  console.log('\nRestart your server to apply. Or set DEV_INSPECTOR_KEY in .env');
159
155
  } else {
160
156
  console.error(`\n✗ License validation failed: ${result.error || 'Unknown error'}`);
@@ -105,7 +105,7 @@ var crypto = __toESM(require("crypto"));
105
105
  var os = __toESM(require("os"));
106
106
  var fs = __toESM(require("fs"));
107
107
  var path = __toESM(require("path"));
108
- var VALIDATE_URL = "https://gate.bcdflow.net/api/v1/license/validate";
108
+ var VALIDATE_URL = "https://gate.bcdflow.net/api/license/validate";
109
109
  var CACHE_DIR = path.join(os.homedir(), ".dev-inspector");
110
110
  var CACHE_FILE = path.join(CACHE_DIR, "license-cache.json");
111
111
  var CACHE_TTL = 24 * 60 * 60 * 1e3;
@@ -117,14 +117,6 @@ var FREE_STATUS = {
117
117
  checkedAt: Date.now()
118
118
  };
119
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
120
  function readCache(key) {
129
121
  try {
130
122
  if (!fs.existsSync(CACHE_FILE)) return null;
@@ -155,12 +147,8 @@ function writeCache(key, status) {
155
147
  }
156
148
  }
157
149
  async function remoteValidate(key) {
158
- const body = JSON.stringify({
159
- key,
160
- package: "dev-inspector",
161
- version: "2.0.0",
162
- machineId: getMachineId()
163
- });
150
+ var _a;
151
+ const body = JSON.stringify({ key });
164
152
  const controller = new AbortController();
165
153
  const timer = setTimeout(() => controller.abort(), VALIDATE_TIMEOUT);
166
154
  try {
@@ -175,10 +163,11 @@ async function remoteValidate(key) {
175
163
  return { ...FREE_STATUS, checkedAt: Date.now() };
176
164
  }
177
165
  const data = await res.json();
166
+ const isValid = !!data.valid;
178
167
  return {
179
- tier: data.tier === "pro" ? "pro" : "free",
180
- valid: !!data.valid,
181
- expiresAt: data.expiresAt || null,
168
+ tier: isValid ? "pro" : "free",
169
+ valid: isValid,
170
+ expiresAt: ((_a = data.license) == null ? void 0 : _a.expiresAt) || null,
182
171
  checkedAt: Date.now()
183
172
  };
184
173
  } catch {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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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",
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/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({ key });\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 // gate \uC11C\uBC84 \uC751\uB2F5: { valid, license: { status, expiresAt, ... } }\n const isValid = !!data.valid;\n return {\n tier: isValid ? 'pro' : 'free',\n valid: isValid,\n expiresAt: data.license?.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
6
  "names": ["getExpress"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bcdflow/dev-inspector",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
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
5
  "main": "dist/middleware/index.js",
6
6
  "bin": {
@@ -17,7 +17,15 @@
17
17
  "clean": "rm -rf dist",
18
18
  "prepublishOnly": "npm run build"
19
19
  },
20
- "keywords": ["mcp", "dom-inspector", "claude", "ai", "express-middleware", "accessibility", "devtools"],
20
+ "keywords": [
21
+ "mcp",
22
+ "dom-inspector",
23
+ "claude",
24
+ "ai",
25
+ "express-middleware",
26
+ "accessibility",
27
+ "devtools"
28
+ ],
21
29
  "license": "MIT",
22
30
  "peerDependencies": {
23
31
  "express": ">=4.0.0"
@@ -28,6 +36,7 @@
28
36
  }
29
37
  },
30
38
  "devDependencies": {
39
+ "@bcdflow/dev-inspector": "^2.0.0",
31
40
  "esbuild": "^0.24.0",
32
41
  "typescript": "^5.7.0"
33
42
  },