@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 ADDED
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * dev-inspector CLI
4
+ * Usage:
5
+ * npx dev-inspector setup — Claude Code에 MCP 서버 등록
6
+ * npx dev-inspector setup --global — 전역 등록
7
+ * npx dev-inspector activate <key> — 라이선스 키 활성화
8
+ * npx dev-inspector status — 라이선스 상태 확인
9
+ * npx dev-inspector mcp — MCP 서버 직접 실행
10
+ */
11
+ const { execSync, spawn } = require('child_process');
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+ const os = require('os');
15
+ const crypto = require('crypto');
16
+
17
+ const command = process.argv[2];
18
+ const CONFIG_DIR = path.join(os.homedir(), '.dev-inspector');
19
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
20
+ const CACHE_FILE = path.join(CONFIG_DIR, 'license-cache.json');
21
+
22
+ switch (command) {
23
+ case 'setup':
24
+ setup();
25
+ break;
26
+ case 'activate':
27
+ activate();
28
+ break;
29
+ case 'status':
30
+ status();
31
+ break;
32
+ case 'mcp':
33
+ runMCP();
34
+ break;
35
+ default:
36
+ console.log(`
37
+ dev-inspector — DOM inspector + MCP bridge for AI coding assistants
38
+
39
+ Usage:
40
+ npx dev-inspector setup Register MCP server with Claude Code
41
+ npx dev-inspector setup --global Register globally (all projects)
42
+ npx dev-inspector activate <key> Activate Pro license
43
+ npx dev-inspector status Show license status
44
+ npx dev-inspector mcp Run MCP server directly (debug)
45
+
46
+ Quick start:
47
+ 1. npm install -D @bcdflow/dev-inspector
48
+ 2. Add to your Express app:
49
+ const { devInspector } = require('@bcdflow/dev-inspector');
50
+ app.use(devInspector());
51
+ 3. npx dev-inspector setup
52
+ 4. (Optional) npx dev-inspector activate DI-XXXX-XXXX-XXXX
53
+ `);
54
+ }
55
+
56
+ function setup() {
57
+ const isGlobal = process.argv.includes('--global');
58
+ const mcpServerPath = path.resolve(__dirname, '..', 'dist', 'mcp', 'server.js');
59
+
60
+ try {
61
+ execSync('claude --version', { stdio: 'pipe' });
62
+ } catch {
63
+ console.error('Error: Claude CLI not found. Install: npm install -g @anthropic-ai/claude-code');
64
+ process.exit(1);
65
+ }
66
+
67
+ if (!fs.existsSync(mcpServerPath)) {
68
+ console.log('Building MCP server...');
69
+ try {
70
+ execSync('npm run build', { cwd: path.resolve(__dirname, '..'), stdio: 'inherit' });
71
+ } catch {
72
+ console.error('Error: Build failed. Run `npm run build` in the dev-inspector directory.');
73
+ process.exit(1);
74
+ }
75
+ }
76
+
77
+ // 라이선스 키가 있으면 환경변수로 전달
78
+ const licenseKey = loadKey();
79
+ const scope = isGlobal ? '--scope user' : '--scope project';
80
+ let cmd = `claude mcp add dev-inspector ${scope}`;
81
+ if (licenseKey) {
82
+ cmd += ` --env DEV_INSPECTOR_KEY=${licenseKey}`;
83
+ }
84
+ cmd += ` -- node "${mcpServerPath}"`;
85
+
86
+ console.log(`Registering MCP server (${isGlobal ? 'global' : 'project'})...`);
87
+ try {
88
+ execSync(cmd, { stdio: 'inherit' });
89
+ console.log('\nMCP server registered successfully!');
90
+ console.log('\nArchitecture:');
91
+ console.log(' Claude Code → MCP stdio → Express API (SSE+REST) → Client JS → DOM');
92
+ console.log('\nMake sure your Express app includes: app.use(devInspector())');
93
+ if (licenseKey) {
94
+ console.log(`License key: ${licenseKey.slice(0, 7)}...${licenseKey.slice(-4)}`);
95
+ }
96
+ } catch (err) {
97
+ console.error('Failed to register MCP server:', err.message);
98
+ process.exit(1);
99
+ }
100
+ }
101
+
102
+ function activate() {
103
+ const key = process.argv[3];
104
+ if (!key) {
105
+ console.error('Usage: npx dev-inspector activate <license-key>');
106
+ console.error('Example: npx dev-inspector activate DI-XXXX-XXXX-XXXX');
107
+ process.exit(1);
108
+ }
109
+
110
+ // 키 저장
111
+ if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
112
+ let config = {};
113
+ try {
114
+ if (fs.existsSync(CONFIG_FILE)) config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
115
+ } catch {}
116
+ config.licenseKey = key;
117
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
118
+
119
+ console.log(`License key saved to ${CONFIG_FILE}`);
120
+
121
+ // 즉시 검증 시도
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
+ });
129
+
130
+ const https = require('https');
131
+ const req = https.request('https://gate.bcdflow.net/api/v1/license/validate', {
132
+ method: 'POST',
133
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
134
+ timeout: 5000,
135
+ }, (res) => {
136
+ let data = '';
137
+ res.on('data', c => data += c);
138
+ res.on('end', () => {
139
+ try {
140
+ const result = JSON.parse(data);
141
+ if (result.valid) {
142
+ // 캐시 저장
143
+ const keyHash = crypto.createHash('sha256').update(key).digest('hex').slice(0, 16);
144
+ let cache = {};
145
+ try {
146
+ if (fs.existsSync(CACHE_FILE)) cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
147
+ } catch {}
148
+ cache[keyHash] = {
149
+ tier: result.tier || 'pro',
150
+ valid: true,
151
+ expiresAt: result.expiresAt || null,
152
+ checkedAt: Date.now(),
153
+ };
154
+ fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
155
+
156
+ console.log(`\n✓ License activated: ${result.tier || 'Pro'}`);
157
+ if (result.expiresAt) console.log(` Expires: ${result.expiresAt.split('T')[0]}`);
158
+ console.log('\nRestart your server to apply. Or set DEV_INSPECTOR_KEY in .env');
159
+ } else {
160
+ console.error(`\n✗ License validation failed: ${result.error || 'Unknown error'}`);
161
+ }
162
+ } catch {
163
+ console.error('✗ Invalid server response');
164
+ }
165
+ });
166
+ });
167
+ req.on('error', (err) => {
168
+ console.log('\nCould not reach license server (offline?).');
169
+ console.log('Key saved locally — will validate on next server start.');
170
+ });
171
+ req.on('timeout', () => {
172
+ req.destroy();
173
+ console.log('\nLicense server timeout. Key saved — will validate later.');
174
+ });
175
+ req.write(body);
176
+ req.end();
177
+ }
178
+
179
+ function status() {
180
+ const key = loadKey();
181
+ if (!key) {
182
+ console.log('No license key configured.');
183
+ console.log('Free tier: 11 tools available.');
184
+ console.log('\nTo activate: npx dev-inspector activate <key>');
185
+ console.log('Or set DEV_INSPECTOR_KEY environment variable.');
186
+ return;
187
+ }
188
+
189
+ console.log(`Key: ${key.slice(0, 7)}...${key.slice(-4)}`);
190
+
191
+ // 캐시 확인
192
+ try {
193
+ if (fs.existsSync(CACHE_FILE)) {
194
+ const cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
195
+ const keyHash = crypto.createHash('sha256').update(key).digest('hex').slice(0, 16);
196
+ const entry = cache[keyHash];
197
+ if (entry) {
198
+ const age = Date.now() - entry.checkedAt;
199
+ const ageHours = Math.round(age / 3600000);
200
+ console.log(`Tier: ${entry.tier}`);
201
+ console.log(`Valid: ${entry.valid}`);
202
+ if (entry.expiresAt) console.log(`Expires: ${entry.expiresAt.split('T')[0]}`);
203
+ console.log(`Last checked: ${ageHours}h ago`);
204
+ if (age > 24 * 3600000) console.log('(Cache expired — will revalidate on next server start)');
205
+ return;
206
+ }
207
+ }
208
+ } catch {}
209
+ console.log('No cached validation. Will validate on next server start.');
210
+ }
211
+
212
+ function runMCP() {
213
+ const mcpServerPath = path.resolve(__dirname, '..', 'dist', 'mcp', 'server.js');
214
+ if (!fs.existsSync(mcpServerPath)) {
215
+ console.error('Error: MCP server not built. Run `npm run build` first.');
216
+ process.exit(1);
217
+ }
218
+ const child = spawn('node', [mcpServerPath], {
219
+ stdio: 'inherit',
220
+ env: { ...process.env },
221
+ });
222
+ child.on('exit', (code) => process.exit(code || 0));
223
+ }
224
+
225
+ function loadKey() {
226
+ // env > config file
227
+ if (process.env.DEV_INSPECTOR_KEY) return process.env.DEV_INSPECTOR_KEY;
228
+ try {
229
+ if (fs.existsSync(CONFIG_FILE)) {
230
+ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
231
+ return config.licenseKey || null;
232
+ }
233
+ } catch {}
234
+ return null;
235
+ }
236
+
237
+ function getMachineId() {
238
+ try {
239
+ const raw = os.hostname() + ':' + os.userInfo().username;
240
+ return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);
241
+ } catch {
242
+ return 'unknown';
243
+ }
244
+ }