bluelamp 6.3.4 → 6.3.6
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 +14 -0
- package/bin/bluelamp-vscode-base +310 -1
- package/lib/auth/InteractiveLogin.js +253 -1
- package/lib/auth/PortalAuthClient.js +186 -1
- package/lib/auth/TokenManager.js +258 -1
- package/lib/i18n/index.js +75 -1
- package/lib/mcp-server/index.cjs +1157 -1
- package/lib/mcp-server/portal-api-client.cjs +934 -1
- package/lib/utils/logger.js +83 -1
- package/package.json +21 -7
- package/scripts/build.js +354 -0
- package/scripts/generate-commands.js +44 -1
- package/scripts/setup-mcps.js +107 -1
package/lib/utils/logger.js
CHANGED
|
@@ -1 +1,83 @@
|
|
|
1
|
-
|
|
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.
|
|
3
|
+
"version": "6.3.6",
|
|
4
4
|
"description": "BlueLamp MCP Server - AI-driven fully automated development environment for Claude Code",
|
|
5
|
-
"main": "
|
|
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
|
+
}
|
package/scripts/build.js
ADDED
|
@@ -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
|
-
|
|
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})`);
|
package/scripts/setup-mcps.js
CHANGED
|
@@ -1,2 +1,108 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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();
|