bluelamp 6.3.4 → 6.3.5

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.
@@ -1 +1,83 @@
1
- const LOG_LEVELS={debug:0,info:1,warn:2,error:3,silent:4},LOG_LEVEL=process.env.MUSUHI_LOG_LEVEL||"info",currentLevel=LOG_LEVELS[LOG_LEVEL]??LOG_LEVELS.info;function formatMessage(e,r){return`[${(new Date).toISOString()}] [${e.toUpperCase()}] ${r}`}const logger={debug(e){currentLevel<=LOG_LEVELS.debug&&console.error(formatMessage("debug",e))},info(e){currentLevel<=LOG_LEVELS.info&&console.error(formatMessage("info",e))},warn(e){currentLevel<=LOG_LEVELS.warn&&console.warn(formatMessage("warn",e))},error(e){currentLevel<=LOG_LEVELS.error&&console.error(formatMessage("error",e))},getLevel:()=>LOG_LEVEL};module.exports={logger:logger,LOG_LEVELS:LOG_LEVELS};
1
+ /**
2
+ * むすひ VSCode Logger
3
+ * 環境変数ベースのログレベル制御
4
+ *
5
+ * @module logger
6
+ */
7
+
8
+ const LOG_LEVELS = {
9
+ debug: 0,
10
+ info: 1,
11
+ warn: 2,
12
+ error: 3,
13
+ silent: 4
14
+ };
15
+
16
+ const LOG_LEVEL = process.env.MUSUHI_LOG_LEVEL || 'info';
17
+ const currentLevel = LOG_LEVELS[LOG_LEVEL] ?? LOG_LEVELS.info;
18
+
19
+ /**
20
+ * タイムスタンプ付きログメッセージを生成
21
+ * @param {string} level - ログレベル
22
+ * @param {string} message - メッセージ
23
+ * @returns {string} フォーマット済みメッセージ
24
+ */
25
+ function formatMessage(level, message) {
26
+ const timestamp = new Date().toISOString();
27
+ return `[${timestamp}] [${level.toUpperCase()}] ${message}`;
28
+ }
29
+
30
+ /**
31
+ * ログ出力関数
32
+ */
33
+ const logger = {
34
+ /**
35
+ * デバッグログ
36
+ * @param {string} message - メッセージ
37
+ */
38
+ debug(message) {
39
+ if (currentLevel <= LOG_LEVELS.debug) {
40
+ console.error(formatMessage('debug', message));
41
+ }
42
+ },
43
+
44
+ /**
45
+ * 情報ログ
46
+ * @param {string} message - メッセージ
47
+ */
48
+ info(message) {
49
+ if (currentLevel <= LOG_LEVELS.info) {
50
+ console.error(formatMessage('info', message));
51
+ }
52
+ },
53
+
54
+ /**
55
+ * 警告ログ
56
+ * @param {string} message - メッセージ
57
+ */
58
+ warn(message) {
59
+ if (currentLevel <= LOG_LEVELS.warn) {
60
+ console.warn(formatMessage('warn', message));
61
+ }
62
+ },
63
+
64
+ /**
65
+ * エラーログ
66
+ * @param {string} message - メッセージ
67
+ */
68
+ error(message) {
69
+ if (currentLevel <= LOG_LEVELS.error) {
70
+ console.error(formatMessage('error', message));
71
+ }
72
+ },
73
+
74
+ /**
75
+ * ログレベルを取得
76
+ * @returns {string} 現在のログレベル
77
+ */
78
+ getLevel() {
79
+ return LOG_LEVEL;
80
+ }
81
+ };
82
+
83
+ module.exports = { logger, LOG_LEVELS };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "bluelamp",
3
- "version": "6.3.4",
3
+ "version": "6.3.5",
4
4
  "description": "BlueLamp MCP Server - AI-driven fully automated development environment for Claude Code",
5
- "main": "./lib/mcp-server/index.cjs",
5
+ "main": "lib/mcp-server/index.cjs",
6
6
  "bin": {
7
7
  "bluelamp1": "./bin/bluelamp1",
8
8
  "bluelamp2": "./bin/bluelamp2",
@@ -205,6 +205,13 @@
205
205
  "bluelamp199": "./bin/bluelamp199",
206
206
  "bluelamp200": "./bin/bluelamp200"
207
207
  },
208
+ "scripts": {
209
+ "postinstall": "node scripts/generate-commands.js && node scripts/setup-mcps.js",
210
+ "test": "jest",
211
+ "test:coverage": "jest --coverage",
212
+ "test:mcp": "node test-mcp.js",
213
+ "build": "node scripts/build.js"
214
+ },
208
215
  "keywords": [
209
216
  "vscode",
210
217
  "bluelamp",
@@ -230,14 +237,21 @@
230
237
  "files": [
231
238
  "bin/",
232
239
  "lib/",
233
- "scripts/"
240
+ "scripts/",
241
+ "README.md"
234
242
  ],
235
- "scripts": {
236
- "postinstall": "node scripts/generate-commands.js && node scripts/setup-mcps.js"
237
- },
238
243
  "preferGlobal": true,
239
244
  "repository": {
240
245
  "type": "git",
241
246
  "url": "https://github.com/yamatovision/bluelamp-cli.git"
247
+ },
248
+ "homepage": "https://github.com/yamatovision/bluelamp-cli",
249
+ "bugs": {
250
+ "url": "https://github.com/yamatovision/bluelamp-cli/issues"
251
+ },
252
+ "devDependencies": {
253
+ "javascript-obfuscator": "^4.1.1",
254
+ "jest": "^30.2.0",
255
+ "terser": "^5.44.0"
242
256
  }
243
- }
257
+ }
@@ -0,0 +1,354 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * むすひ CLI - Production Build Script
4
+ *
5
+ * 2段階難読化戦略:
6
+ * - Terser: 一般ファイル(軽量縮小化)
7
+ * - javascript-obfuscator HIGH: 機密ファイル(強力な保護)
8
+ */
9
+
10
+ const fs = require('fs').promises;
11
+ const path = require('path');
12
+
13
+ const PROJECT_ROOT = path.dirname(__dirname);
14
+ const SRC_DIR = PROJECT_ROOT; // 現在はsrc/なし、後で移行
15
+ const DIST_DIR = path.join(PROJECT_ROOT, 'dist');
16
+
17
+ // 機密ファイル(HIGH難読化対象)
18
+ const SENSITIVE_FILES = [
19
+ 'lib/auth/InteractiveLogin.js',
20
+ 'lib/auth/PortalAuthClient.js',
21
+ 'lib/auth/TokenManager.js',
22
+ 'lib/mcp-server/portal-api-client.cjs',
23
+ 'bin/bluelamp-vscode-base',
24
+ 'scripts/playwright-launch.js'
25
+ ];
26
+
27
+ // ビルド除外ファイル(dev専用、distに含めない)
28
+ const BUILD_EXCLUDE_FILES = [
29
+ 'build.js'
30
+ ];
31
+
32
+ // 非処理ファイル(そのままコピー)
33
+ const COPY_ONLY_FILES = [
34
+ 'lib/agents/むすひ.md',
35
+ 'lib/mcp-password-manager/index.js'
36
+ ];
37
+
38
+ // HIGH難読化設定(機密ファイル用)
39
+ const HIGH_OBFUSCATION_OPTIONS = {
40
+ compact: true,
41
+ controlFlowFlattening: true,
42
+ controlFlowFlatteningThreshold: 0.75,
43
+ deadCodeInjection: true,
44
+ deadCodeInjectionThreshold: 0.4,
45
+ debugProtection: true,
46
+ debugProtectionInterval: 4000,
47
+ disableConsoleOutput: false,
48
+ identifierNamesGenerator: 'hexadecimal',
49
+ log: false,
50
+ numbersToExpressions: true,
51
+ renameGlobals: true,
52
+ selfDefending: true,
53
+ simplify: true,
54
+ splitStrings: true,
55
+ splitStringsChunkLength: 5,
56
+ stringArray: true,
57
+ stringArrayCallsTransform: true,
58
+ stringArrayCallsTransformThreshold: 0.8,
59
+ stringArrayEncoding: ['base64'],
60
+ stringArrayIndexShift: true,
61
+ stringArrayRotate: true,
62
+ stringArrayShuffle: true,
63
+ stringArrayWrappersCount: 2,
64
+ stringArrayWrappersChainedCalls: true,
65
+ stringArrayWrappersParametersMaxCount: 4,
66
+ stringArrayWrappersType: 'function',
67
+ stringArrayThreshold: 0.8,
68
+ transformObjectKeys: true,
69
+ unicodeEscapeSequence: false
70
+ };
71
+
72
+ // Terser設定(一般ファイル用)
73
+ const TERSER_OPTIONS = {
74
+ compress: {
75
+ dead_code: true,
76
+ drop_console: false,
77
+ drop_debugger: true,
78
+ passes: 2
79
+ },
80
+ mangle: {
81
+ reserved: ['require', 'module', 'exports', 'process', '__dirname', '__filename']
82
+ },
83
+ format: {
84
+ comments: false
85
+ }
86
+ };
87
+
88
+ class Logger {
89
+ static info(msg) { console.log(`\x1b[36m[INFO]\x1b[0m ${msg}`); }
90
+ static success(msg) { console.log(`\x1b[32m[OK]\x1b[0m ${msg}`); }
91
+ static warn(msg) { console.log(`\x1b[33m[WARN]\x1b[0m ${msg}`); }
92
+ static error(msg) { console.log(`\x1b[31m[ERROR]\x1b[0m ${msg}`); }
93
+ }
94
+
95
+ async function ensureDir(dirPath) {
96
+ try {
97
+ await fs.mkdir(dirPath, { recursive: true });
98
+ } catch (e) {
99
+ if (e.code !== 'EEXIST') throw e;
100
+ }
101
+ }
102
+
103
+ async function cleanDir(dirPath) {
104
+ try {
105
+ await fs.rm(dirPath, { recursive: true, force: true });
106
+ } catch (e) {
107
+ // ignore
108
+ }
109
+ await ensureDir(dirPath);
110
+ }
111
+
112
+ function isSensitiveFile(relativePath) {
113
+ return SENSITIVE_FILES.some(sf => relativePath.endsWith(sf) || relativePath === sf);
114
+ }
115
+
116
+ function isCopyOnlyFile(relativePath) {
117
+ return COPY_ONLY_FILES.some(cf => relativePath.endsWith(cf) || relativePath === cf);
118
+ }
119
+
120
+ function isJavaScriptFile(filePath) {
121
+ // .js, .cjs, または拡張子なしのbin/ファイル(bluelamp-vscode-base)
122
+ if (filePath.endsWith('.js') || filePath.endsWith('.cjs')) return true;
123
+ // bin/bluelamp-vscode-base は拡張子がないがJSファイル
124
+ if (filePath.includes('bin/bluelamp-vscode-base')) return true;
125
+ return false;
126
+ }
127
+
128
+ async function processWithTerser(code, filePath) {
129
+ try {
130
+ const { minify } = require('terser');
131
+ const result = await minify(code, TERSER_OPTIONS);
132
+ if (result.error) throw result.error;
133
+ return result.code;
134
+ } catch (error) {
135
+ Logger.warn(`Terser failed for ${filePath}, using original: ${error.message}`);
136
+ return code;
137
+ }
138
+ }
139
+
140
+ function processWithObfuscator(code) {
141
+ const JavaScriptObfuscator = require('javascript-obfuscator');
142
+ const result = JavaScriptObfuscator.obfuscate(code, HIGH_OBFUSCATION_OPTIONS);
143
+ return result.getObfuscatedCode();
144
+ }
145
+
146
+ async function processFile(srcPath, destPath, relativePath) {
147
+ const content = await fs.readFile(srcPath, 'utf8');
148
+
149
+ // 非JSファイルまたはコピーのみファイル
150
+ if (!isJavaScriptFile(srcPath) || isCopyOnlyFile(relativePath)) {
151
+ await ensureDir(path.dirname(destPath));
152
+ await fs.writeFile(destPath, content, 'utf8');
153
+ // bin/ および scripts/ ファイルの実行権限付与(コピーパスでも必須)
154
+ // 2026-04-21: bluelamp1..200 ラッパーが 644 で publish され `permission denied` が発生した問題への恒久対策
155
+ if (relativePath.startsWith('bin/') || relativePath.startsWith('scripts/')) {
156
+ await fs.chmod(destPath, 0o755);
157
+ }
158
+ Logger.info(`Copy: ${relativePath}`);
159
+ return 'copy';
160
+ }
161
+
162
+ // shebang行の抽出
163
+ let shebang = '';
164
+ let codeToProcess = content;
165
+ if (content.startsWith('#!')) {
166
+ const idx = content.indexOf('\n');
167
+ shebang = content.substring(0, idx + 1);
168
+ codeToProcess = content.substring(idx + 1);
169
+ }
170
+
171
+ let processedCode;
172
+ let processType;
173
+
174
+ if (isSensitiveFile(relativePath)) {
175
+ // 機密ファイル: HIGH難読化
176
+ processedCode = processWithObfuscator(codeToProcess);
177
+ processType = 'HIGH';
178
+ Logger.success(`HIGH obfuscate: ${relativePath}`);
179
+ } else {
180
+ // 一般ファイル: Terser縮小化
181
+ processedCode = await processWithTerser(codeToProcess, relativePath);
182
+ processType = 'Terser';
183
+ Logger.info(`Terser minify: ${relativePath}`);
184
+ }
185
+
186
+ const finalCode = shebang + processedCode;
187
+
188
+ await ensureDir(path.dirname(destPath));
189
+ await fs.writeFile(destPath, finalCode, 'utf8');
190
+
191
+ // bin/ および scripts/ ファイルの実行権限復元
192
+ if (relativePath.startsWith('bin/') || relativePath.startsWith('scripts/')) {
193
+ await fs.chmod(destPath, 0o755);
194
+ }
195
+
196
+ return processType;
197
+ }
198
+
199
+ async function collectFiles(dir, baseDir = dir) {
200
+ const files = [];
201
+ const entries = await fs.readdir(dir, { withFileTypes: true });
202
+
203
+ for (const entry of entries) {
204
+ const fullPath = path.join(dir, entry.name);
205
+ const relativePath = path.relative(baseDir, fullPath);
206
+
207
+ // 除外パターン
208
+ if (entry.name.startsWith('.') ||
209
+ entry.name === 'node_modules' ||
210
+ entry.name === 'dist' ||
211
+ entry.name === 'release' ||
212
+ entry.name === 'docs' ||
213
+ BUILD_EXCLUDE_FILES.includes(entry.name) ||
214
+ entry.name === 'test-mcp.js' ||
215
+ entry.name === 'test-mcp-api.js' ||
216
+ entry.name.endsWith('.md') && !COPY_ONLY_FILES.includes(relativePath)) {
217
+ continue;
218
+ }
219
+
220
+ if (entry.isDirectory()) {
221
+ const subFiles = await collectFiles(fullPath, baseDir);
222
+ files.push(...subFiles);
223
+ } else {
224
+ files.push({ fullPath, relativePath });
225
+ }
226
+ }
227
+
228
+ return files;
229
+ }
230
+
231
+ async function main() {
232
+ const startTime = Date.now();
233
+
234
+ console.log('');
235
+ console.log('╔═══════════════════════════════════════════════════════════╗');
236
+ console.log('║ むすひ CLI - Production Build ║');
237
+ console.log('║ 2-Stage Obfuscation: Terser + HIGH ║');
238
+ console.log('╚═══════════════════════════════════════════════════════════╝');
239
+ console.log('');
240
+
241
+ // 依存関係チェック
242
+ try {
243
+ require('terser');
244
+ require('javascript-obfuscator');
245
+ } catch (e) {
246
+ Logger.error('Missing dependencies. Run: npm install');
247
+ process.exit(1);
248
+ }
249
+
250
+ // dist/をクリーン
251
+ Logger.info('Cleaning dist/...');
252
+ await cleanDir(DIST_DIR);
253
+
254
+ // 対象ファイル収集
255
+ Logger.info('Collecting files...');
256
+
257
+ const targetDirs = ['bin', 'lib', 'scripts'];
258
+ const allFiles = [];
259
+
260
+ for (const dir of targetDirs) {
261
+ const dirPath = path.join(SRC_DIR, dir);
262
+ try {
263
+ const files = await collectFiles(dirPath, SRC_DIR);
264
+ allFiles.push(...files);
265
+ } catch (e) {
266
+ Logger.warn(`Directory not found: ${dir}`);
267
+ }
268
+ }
269
+
270
+ Logger.info(`Found ${allFiles.length} files to process`);
271
+ console.log('');
272
+
273
+ // ファイル処理
274
+ const stats = { terser: 0, high: 0, copy: 0, error: 0 };
275
+
276
+ for (const file of allFiles) {
277
+ const destPath = path.join(DIST_DIR, file.relativePath);
278
+
279
+ try {
280
+ const type = await processFile(file.fullPath, destPath, file.relativePath);
281
+ if (type === 'Terser') stats.terser++;
282
+ else if (type === 'HIGH') stats.high++;
283
+ else stats.copy++;
284
+ } catch (error) {
285
+ Logger.error(`Failed: ${file.relativePath} - ${error.message}`);
286
+ stats.error++;
287
+ }
288
+ }
289
+
290
+ // package.json生成(dist用に調整)
291
+ Logger.info('Generating dist/package.json...');
292
+ const pkgPath = path.join(SRC_DIR, 'package.json');
293
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
294
+
295
+ // binパスをdist/相対に変更しない(npm publishはdist/から行う)
296
+ const distPkg = {
297
+ name: pkg.name,
298
+ version: pkg.version,
299
+ description: pkg.description,
300
+ main: pkg.main.replace('lib/', './lib/'),
301
+ bin: {},
302
+ keywords: pkg.keywords,
303
+ author: pkg.author,
304
+ license: pkg.license,
305
+ dependencies: pkg.dependencies,
306
+ engines: pkg.engines,
307
+ files: ['bin/', 'lib/', 'scripts/'],
308
+ scripts: {
309
+ postinstall: 'node scripts/generate-commands.js && node scripts/setup-mcps.js'
310
+ },
311
+ preferGlobal: pkg.preferGlobal,
312
+ repository: pkg.repository
313
+ };
314
+
315
+ // binパスを相対パスに
316
+ for (const [cmd, binPath] of Object.entries(pkg.bin)) {
317
+ distPkg.bin[cmd] = binPath.replace('./bin/', './bin/');
318
+ }
319
+
320
+ await fs.writeFile(
321
+ path.join(DIST_DIR, 'package.json'),
322
+ JSON.stringify(distPkg, null, 2),
323
+ 'utf8'
324
+ );
325
+
326
+ // 結果表示
327
+ const duration = ((Date.now() - startTime) / 1000).toFixed(2);
328
+
329
+ console.log('');
330
+ console.log('╔═══════════════════════════════════════════════════════════╗');
331
+ console.log('║ Build Complete ║');
332
+ console.log('╠═══════════════════════════════════════════════════════════╣');
333
+ console.log(`║ Terser (general): ${String(stats.terser).padStart(3)} files ║`);
334
+ console.log(`║ HIGH (sensitive): ${String(stats.high).padStart(3)} files ║`);
335
+ console.log(`║ Copy only: ${String(stats.copy).padStart(3)} files ║`);
336
+ console.log(`║ Errors: ${String(stats.error).padStart(3)} files ║`);
337
+ console.log(`║ Time: ${duration.padStart(5)}s ║`);
338
+ console.log('╠═══════════════════════════════════════════════════════════╣');
339
+ console.log('║ Output: dist/ ║');
340
+ console.log('║ ║');
341
+ console.log('║ To publish: ║');
342
+ console.log('║ cd dist && npm publish ║');
343
+ console.log('╚═══════════════════════════════════════════════════════════╝');
344
+ console.log('');
345
+
346
+ if (stats.error > 0) {
347
+ process.exit(1);
348
+ }
349
+ }
350
+
351
+ main().catch(e => {
352
+ Logger.error(e.message);
353
+ process.exit(1);
354
+ });
@@ -1,2 +1,45 @@
1
1
  #!/usr/bin/env node
2
- const fs=require("fs"),path=require("path"),binDir=path.join(__dirname,"..","bin"),baseScript=path.join(binDir,"bluelamp-vscode-base"),MAX_SLOTS=200;console.log("🔵 BlueLamp コマンド生成開始...\n");let createdCount=0;for(let e=1;e<=200;e++){const n=`bluelamp${e}`,o=path.join(binDir,n),l="#!/usr/bin/env node\nrequire('./bluelamp-vscode-base');\n";fs.writeFileSync(o,l),fs.chmodSync(o,"755"),createdCount++,e%10==0&&console.log(` ✅ bluelamp1〜${e} 作成完了`)}fs.existsSync(baseScript)&&fs.chmodSync(baseScript,"755"),console.log(`\n✨ ${createdCount}個のコマンドファイルを生成しました (bluelamp1〜bluelamp200)`);
2
+
3
+ /**
4
+ * BlueLamp コマンド生成スクリプト
5
+ * bluelamp1〜200 の200個のコマンドファイルを自動生成
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const binDir = path.join(__dirname, '..', 'bin');
12
+ const baseScript = path.join(binDir, 'bluelamp-vscode-base');
13
+
14
+ // 生成するコマンド数(200スロット対応: 1-100グローバル, 101-200個人)
15
+ const MAX_SLOTS = 200;
16
+
17
+ console.log('🔵 BlueLamp コマンド生成開始...\n');
18
+
19
+ // bluelamp1〜200 を生成
20
+ let createdCount = 0;
21
+ for (let i = 1; i <= MAX_SLOTS; i++) {
22
+ const commandName = `bluelamp${i}`;
23
+ const commandPath = path.join(binDir, commandName);
24
+
25
+ // ラッパースクリプトを作成
26
+ const content = `#!/usr/bin/env node
27
+ require('./bluelamp-vscode-base');
28
+ `;
29
+
30
+ fs.writeFileSync(commandPath, content);
31
+ fs.chmodSync(commandPath, '755');
32
+ createdCount++;
33
+
34
+ // 進捗表示(10個ごと)
35
+ if (i % 10 === 0) {
36
+ console.log(` ✅ bluelamp1〜${i} 作成完了`);
37
+ }
38
+ }
39
+
40
+ // ベーススクリプトにも実行権限を付与
41
+ if (fs.existsSync(baseScript)) {
42
+ fs.chmodSync(baseScript, '755');
43
+ }
44
+
45
+ console.log(`\n✨ ${createdCount}個のコマンドファイルを生成しました (bluelamp1〜bluelamp${MAX_SLOTS})`);
@@ -1,2 +1,108 @@
1
1
  #!/usr/bin/env node
2
- const fs=require("fs"),path=require("path"),os=require("os"),crypto=require("crypto"),MCP_JSON_PATH=path.join(os.homedir(),".mcp.json"),BLUELAMP_DIR=path.join(os.homedir(),".bluelamp"),REGISTRY_PATH=path.join(BLUELAMP_DIR,"registry.json"),VAULT_KEY_PATH=path.join(BLUELAMP_DIR,"vault.key"),PASSWORD_MANAGER_ENTRY=path.join(__dirname,"..","lib","mcp-password-manager","index.js");function setup(){if(console.log("\n📦 BlueLamp MCP セットアップ開始...\n"),fs.existsSync(BLUELAMP_DIR)||fs.mkdirSync(BLUELAMP_DIR,{recursive:!0,mode:448}),!fs.existsSync(VAULT_KEY_PATH)){const e=crypto.randomBytes(32).toString("hex");fs.writeFileSync(VAULT_KEY_PATH,e,{mode:384}),console.log(" 🔑 暗号化キーを生成しました (~/.bluelamp/vault.key)")}const e=path.join(os.homedir(),".playwright-profiles");fs.existsSync(e)||fs.mkdirSync(e,{recursive:!0});let s={};if(fs.existsSync(REGISTRY_PATH))try{s=JSON.parse(fs.readFileSync(REGISTRY_PATH,"utf-8")),console.log(" 📋 既存registry.jsonを検出、マージします")}catch{console.log(" ⚠️ registry.json解析失敗、新規作成します"),s={}}fs.writeFileSync(REGISTRY_PATH,JSON.stringify(s,null,2)+"\n"),console.log(`\n 📋 registry.json 更新完了 (${Object.keys(s).length}サービス)`);let o={mcpServers:{}};if(fs.existsSync(MCP_JSON_PATH))try{o=JSON.parse(fs.readFileSync(MCP_JSON_PATH,"utf8")),o.mcpServers||(o.mcpServers={})}catch{console.log(" ⚠️ ~/.mcp.json 解析失敗、再作成します"),o={mcpServers:{}}}const r=["playwright","playwright-2","playwright-3","password-manager","gmail","ms365","poc-gateway"];let l=0;for(const e of r)o.mcpServers[e]&&(delete o.mcpServers[e],l++);l>0&&console.log(` 🧹 ~/.mcp.json から ${l}個の個別MCP登録を削除`),o.mcpServers.bluelamp||(o.mcpServers.bluelamp={command:"bluelamp1",args:[]},console.log(" 🚀 bluelamp ゲートウェイを ~/.mcp.json に登録")),fs.writeFileSync(MCP_JSON_PATH,JSON.stringify(o,null,2)+"\n"),console.log(" ✅ ~/.mcp.json 更新完了"),console.log("\n✨ BlueLamp MCP セットアップ完了"),console.log(" Gateway統合: bluelamp 1プロセスで全MCPをプロキシ"),console.log(" MCPのインストール: install_from_store + @BlueLampセットアップ で登録してください\n")}setup();
2
+
3
+ /**
4
+ * BlueLamp MCP セットアップ(Gateway統合版)
5
+ *
6
+ * postinstall時に実行:
7
+ * 1. vault key生成(Password Manager用)
8
+ * 2. ~/.bluelamp/registry.json 生成(MCPサービス定義)
9
+ * 3. ~/.mcp.json から個別MCP登録を削除(bluelampのみ残す)
10
+ *
11
+ * Gateway統合後は ~/.mcp.json には bluelamp 1つだけ。
12
+ * 個別MCPはすべて registry.json で管理し、
13
+ * bluelampプロセスがゲートウェイとしてプロキシする。
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const os = require('os');
19
+ const crypto = require('crypto');
20
+
21
+ const MCP_JSON_PATH = path.join(os.homedir(), '.mcp.json');
22
+ const BLUELAMP_DIR = path.join(os.homedir(), '.bluelamp');
23
+ const REGISTRY_PATH = path.join(BLUELAMP_DIR, 'registry.json');
24
+ const VAULT_KEY_PATH = path.join(BLUELAMP_DIR, 'vault.key');
25
+
26
+ // バンドル内パス
27
+ const PASSWORD_MANAGER_ENTRY = path.join(__dirname, '..', 'lib', 'mcp-password-manager', 'index.js');
28
+
29
+ function setup() {
30
+ console.log('\n📦 BlueLamp MCP セットアップ開始...\n');
31
+
32
+ // --- 1. ~/.bluelamp ディレクトリ ---
33
+ if (!fs.existsSync(BLUELAMP_DIR)) {
34
+ fs.mkdirSync(BLUELAMP_DIR, { recursive: true, mode: 0o700 });
35
+ }
36
+
37
+ // --- 2. Password Manager: vault key ---
38
+ if (!fs.existsSync(VAULT_KEY_PATH)) {
39
+ const key = crypto.randomBytes(32).toString('hex');
40
+ fs.writeFileSync(VAULT_KEY_PATH, key, { mode: 0o600 });
41
+ console.log(' 🔑 暗号化キーを生成しました (~/.bluelamp/vault.key)');
42
+ }
43
+
44
+ // --- 3. Playwright profiles directory ---
45
+ const playwrightProfiles = path.join(os.homedir(), '.playwright-profiles');
46
+ if (!fs.existsSync(playwrightProfiles)) {
47
+ fs.mkdirSync(playwrightProfiles, { recursive: true });
48
+ }
49
+
50
+ // --- 4. registry.json 生成 ---
51
+ let registry = {};
52
+ if (fs.existsSync(REGISTRY_PATH)) {
53
+ try {
54
+ registry = JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf-8'));
55
+ console.log(' 📋 既存registry.jsonを検出、マージします');
56
+ } catch {
57
+ console.log(' ⚠️ registry.json解析失敗、新規作成します');
58
+ registry = {};
59
+ }
60
+ }
61
+
62
+ // registry.json 書き込み(MCPは install_from_store + @BlueLampセットアップ で登録)
63
+ fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2) + '\n');
64
+ console.log(`\n 📋 registry.json 更新完了 (${Object.keys(registry).length}サービス)`);
65
+
66
+ // --- 5. ~/.mcp.json: 個別MCP削除 + bluelampゲートウェイ登録 ---
67
+ let mcpConfig = { mcpServers: {} };
68
+ if (fs.existsSync(MCP_JSON_PATH)) {
69
+ try {
70
+ mcpConfig = JSON.parse(fs.readFileSync(MCP_JSON_PATH, 'utf8'));
71
+ if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
72
+ } catch {
73
+ console.log(' ⚠️ ~/.mcp.json 解析失敗、再作成します');
74
+ mcpConfig = { mcpServers: {} };
75
+ }
76
+ }
77
+
78
+ // 旧個別MCP登録を削除
79
+ const toRemove = ['playwright', 'playwright-2', 'playwright-3', 'password-manager', 'gmail', 'ms365', 'poc-gateway'];
80
+ let removed = 0;
81
+ for (const key of toRemove) {
82
+ if (mcpConfig.mcpServers[key]) {
83
+ delete mcpConfig.mcpServers[key];
84
+ removed++;
85
+ }
86
+ }
87
+ if (removed > 0) {
88
+ console.log(` 🧹 ~/.mcp.json から ${removed}個の個別MCP登録を削除`);
89
+ }
90
+
91
+ // bluelampゲートウェイエントリを追加(未登録の場合)
92
+ if (!mcpConfig.mcpServers.bluelamp) {
93
+ mcpConfig.mcpServers.bluelamp = {
94
+ command: 'bluelamp1',
95
+ args: [],
96
+ };
97
+ console.log(' 🚀 bluelamp ゲートウェイを ~/.mcp.json に登録');
98
+ }
99
+
100
+ fs.writeFileSync(MCP_JSON_PATH, JSON.stringify(mcpConfig, null, 2) + '\n');
101
+ console.log(' ✅ ~/.mcp.json 更新完了');
102
+
103
+ console.log('\n✨ BlueLamp MCP セットアップ完了');
104
+ console.log(' Gateway統合: bluelamp 1プロセスで全MCPをプロキシ');
105
+ console.log(' MCPのインストール: install_from_store + @BlueLampセットアップ で登録してください\n');
106
+ }
107
+
108
+ setup();