bluelamp 3.0.8 → 4.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.
Files changed (138) hide show
  1. package/README.md +68 -12
  2. package/bin/bluelamp-base +145 -330
  3. package/bin/bluelamp-hook +33 -0
  4. package/bin/bluelamp1 +2 -0
  5. package/bin/bluelamp10 +1 -373
  6. package/bin/bluelamp11 +1 -373
  7. package/bin/bluelamp12 +1 -373
  8. package/bin/bluelamp13 +1 -373
  9. package/bin/bluelamp14 +1 -373
  10. package/bin/bluelamp15 +1 -373
  11. package/bin/bluelamp16 +1 -373
  12. package/bin/bluelamp17 +1 -373
  13. package/bin/bluelamp18 +1 -373
  14. package/bin/bluelamp2 +1 -373
  15. package/bin/bluelamp3 +1 -373
  16. package/bin/bluelamp4 +1 -373
  17. package/bin/bluelamp5 +1 -373
  18. package/bin/bluelamp6 +1 -373
  19. package/bin/bluelamp7 +1 -373
  20. package/bin/bluelamp8 +1 -373
  21. package/bin/bluelamp9 +1 -373
  22. package/bin/docs/SCOPE_PROGRESS.md +222 -0
  23. package/lib/agents//343/203/226/343/203/253/343/203/274/343/203/251/343/203/263/343/203/227.md +106 -0
  24. package/lib/auth/InteractiveLogin.js +243 -0
  25. package/lib/auth/PortalAuthClient.js +187 -0
  26. package/lib/auth/TokenManager.js +258 -0
  27. package/lib/mcp-server/fetch-prompt.js +61 -0
  28. package/lib/mcp-server/index.cjs +164 -0
  29. package/lib/mcp-server/portal-api-client.cjs +210 -0
  30. package/package.json +24 -108
  31. package/scripts/build-release.js +426 -0
  32. package/scripts/generate-commands.js +40 -0
  33. package/scripts/setup-hook.js +98 -0
  34. package/bin/bluelamp +0 -374
  35. package/bin/bluelamp100 +0 -374
  36. package/bin/bluelamp19 +0 -374
  37. package/bin/bluelamp20 +0 -374
  38. package/bin/bluelamp21 +0 -374
  39. package/bin/bluelamp22 +0 -374
  40. package/bin/bluelamp23 +0 -374
  41. package/bin/bluelamp24 +0 -374
  42. package/bin/bluelamp25 +0 -374
  43. package/bin/bluelamp26 +0 -374
  44. package/bin/bluelamp27 +0 -374
  45. package/bin/bluelamp28 +0 -374
  46. package/bin/bluelamp29 +0 -374
  47. package/bin/bluelamp30 +0 -374
  48. package/bin/bluelamp31 +0 -374
  49. package/bin/bluelamp32 +0 -374
  50. package/bin/bluelamp33 +0 -374
  51. package/bin/bluelamp34 +0 -374
  52. package/bin/bluelamp35 +0 -374
  53. package/bin/bluelamp36 +0 -374
  54. package/bin/bluelamp37 +0 -374
  55. package/bin/bluelamp38 +0 -374
  56. package/bin/bluelamp39 +0 -374
  57. package/bin/bluelamp40 +0 -374
  58. package/bin/bluelamp41 +0 -374
  59. package/bin/bluelamp42 +0 -374
  60. package/bin/bluelamp43 +0 -374
  61. package/bin/bluelamp44 +0 -374
  62. package/bin/bluelamp45 +0 -374
  63. package/bin/bluelamp46 +0 -374
  64. package/bin/bluelamp47 +0 -374
  65. package/bin/bluelamp48 +0 -374
  66. package/bin/bluelamp49 +0 -374
  67. package/bin/bluelamp50 +0 -374
  68. package/bin/bluelamp51 +0 -374
  69. package/bin/bluelamp52 +0 -374
  70. package/bin/bluelamp53 +0 -374
  71. package/bin/bluelamp54 +0 -374
  72. package/bin/bluelamp55 +0 -374
  73. package/bin/bluelamp56 +0 -374
  74. package/bin/bluelamp57 +0 -374
  75. package/bin/bluelamp58 +0 -374
  76. package/bin/bluelamp59 +0 -374
  77. package/bin/bluelamp60 +0 -374
  78. package/bin/bluelamp61 +0 -374
  79. package/bin/bluelamp62 +0 -374
  80. package/bin/bluelamp63 +0 -374
  81. package/bin/bluelamp64 +0 -374
  82. package/bin/bluelamp65 +0 -374
  83. package/bin/bluelamp66 +0 -374
  84. package/bin/bluelamp67 +0 -374
  85. package/bin/bluelamp68 +0 -374
  86. package/bin/bluelamp69 +0 -374
  87. package/bin/bluelamp70 +0 -374
  88. package/bin/bluelamp71 +0 -374
  89. package/bin/bluelamp72 +0 -374
  90. package/bin/bluelamp73 +0 -374
  91. package/bin/bluelamp74 +0 -374
  92. package/bin/bluelamp75 +0 -374
  93. package/bin/bluelamp76 +0 -374
  94. package/bin/bluelamp77 +0 -374
  95. package/bin/bluelamp78 +0 -374
  96. package/bin/bluelamp79 +0 -374
  97. package/bin/bluelamp80 +0 -374
  98. package/bin/bluelamp81 +0 -374
  99. package/bin/bluelamp82 +0 -374
  100. package/bin/bluelamp83 +0 -374
  101. package/bin/bluelamp84 +0 -374
  102. package/bin/bluelamp85 +0 -374
  103. package/bin/bluelamp86 +0 -374
  104. package/bin/bluelamp87 +0 -374
  105. package/bin/bluelamp88 +0 -374
  106. package/bin/bluelamp89 +0 -374
  107. package/bin/bluelamp90 +0 -374
  108. package/bin/bluelamp91 +0 -374
  109. package/bin/bluelamp92 +0 -374
  110. package/bin/bluelamp93 +0 -374
  111. package/bin/bluelamp94 +0 -374
  112. package/bin/bluelamp95 +0 -374
  113. package/bin/bluelamp96 +0 -374
  114. package/bin/bluelamp97 +0 -374
  115. package/bin/bluelamp98 +0 -374
  116. package/bin/bluelamp99 +0 -374
  117. package/config/claude-config-template.json +0 -13
  118. package/lib/mcp-server/dist/InteractiveLogin.js +0 -27
  119. package/lib/mcp-server/dist/PortalAuthClient.js +0 -1
  120. package/lib/mcp-server/dist/TokenManager.js +0 -20
  121. package/lib/mcp-server/dist/bluelamp-system.js +0 -32
  122. package/lib/mcp-server/dist/crypto-utils.js +0 -27
  123. package/lib/mcp-server/dist/database-version.js +0 -33
  124. package/lib/mcp-server/dist/index.js +0 -62
  125. package/lib/mcp-server/dist/plugins/base-plugin.js +0 -24
  126. package/lib/mcp-server/dist/plugins/plugin-manager.js +0 -31
  127. package/lib/mcp-server/dist/plugins/services/leonardo-plugin.js +0 -95
  128. package/lib/mcp-server/dist/plugins/services/leonardo-prompt-parser.js +0 -36
  129. package/lib/mcp-server/dist/plugins/services/portal-api-client.js +0 -22
  130. package/lib/mcp-server/dist/plugins/services/portal-plugin.js +0 -52
  131. package/lib/mcp-server/dist/secure-mcp-server.js +0 -76
  132. package/lib/mcp-server/dist/security/ApiKeyManager.js +0 -24
  133. package/lib/mcp-server/dist/security/InteractiveLogin.js +0 -32
  134. package/lib/mcp-server/dist/security/PortalAuthClient.js +0 -1
  135. package/lib/mcp-server/dist/security/SimpleAuthManager.js +0 -1
  136. package/lib/mcp-server/dist/security/SlotAuthClient.js +0 -9
  137. package/lib/mcp-server/dist/security/TokenManager.js +0 -25
  138. package/lib/mcp-server/package.json +0 -21
@@ -0,0 +1,258 @@
1
+ /**
2
+ * TokenManager - Portal統合CLIトークン管理クラス
3
+ * CommonJS版(bluelamp-vscode用)
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+ const crypto = require('crypto');
10
+ const https = require('https');
11
+ const http = require('http');
12
+ const { URL } = require('url');
13
+
14
+ class TokenManager {
15
+ static TOKEN_FILE_PATH = path.join(os.homedir(), '.bluelamp-vscode', 'portal-token.enc');
16
+ static PORTAL_API_URL_PROD = 'https://bluelamp-6clpzmy5pa-an.a.run.app/api/cli/verify';
17
+ static PORTAL_API_URL_LOCAL = 'https://bluelamp-6clpzmy5pa-an.a.run.app/api/cli/verify';
18
+ static ENCRYPTION_KEY = 'bluelamp-vscode-portal-token-key-2024';
19
+ static REQUEST_TIMEOUT = 10000; // 10秒
20
+
21
+ /**
22
+ * 開発環境かどうかを判定
23
+ */
24
+ static isLocalDev() {
25
+ return process.env.PORTAL_URL && process.env.PORTAL_URL.includes('localhost');
26
+ }
27
+
28
+ /**
29
+ * APIエンドポイントを取得
30
+ */
31
+ static getApiUrl(endpoint) {
32
+ // 環境変数から取得(デフォルトは本番)
33
+ const portalUrl = process.env.PORTAL_URL || 'https://bluelamp-6clpzmy5pa-an.a.run.app';
34
+ return portalUrl + endpoint;
35
+ }
36
+
37
+ /**
38
+ * Portal発行CLIトークンを暗号化して保存
39
+ */
40
+ static async saveToken(token, userInfo = {}) {
41
+ try {
42
+ // トークン形式検証
43
+ if (!this.validateTokenFormat(token)) {
44
+ throw new Error('Portal発行のcli_形式トークンのみサポートされています');
45
+ }
46
+
47
+ // 保存データ構造
48
+ const tokenData = {
49
+ token: token,
50
+ savedAt: Date.now(),
51
+ userInfo: userInfo,
52
+ version: '1.0',
53
+ source: 'portal'
54
+ };
55
+
56
+ // 暗号化
57
+ const encrypted = this.encrypt(JSON.stringify(tokenData));
58
+
59
+ // ディレクトリ作成
60
+ const tokenDir = path.dirname(this.TOKEN_FILE_PATH);
61
+ if (!fs.existsSync(tokenDir)) {
62
+ fs.mkdirSync(tokenDir, { recursive: true });
63
+ }
64
+
65
+ // ファイル保存
66
+ fs.writeFileSync(this.TOKEN_FILE_PATH, encrypted);
67
+ console.log(' ✅ トークン保存完了');
68
+
69
+ return true;
70
+
71
+ } catch (error) {
72
+ console.error('❌ トークン保存エラー:', error.message);
73
+ return false;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * 保存されたPortal CLIトークンを読み込み
79
+ */
80
+ static async loadToken() {
81
+ try {
82
+ if (!fs.existsSync(this.TOKEN_FILE_PATH)) {
83
+ return null;
84
+ }
85
+
86
+ // ファイル読み込み
87
+ const encrypted = fs.readFileSync(this.TOKEN_FILE_PATH, 'utf8');
88
+
89
+ // 復号化
90
+ const decrypted = this.decrypt(encrypted);
91
+ const tokenData = JSON.parse(decrypted);
92
+
93
+ // データ構造確認
94
+ if (!tokenData.token || !tokenData.version) {
95
+ return null;
96
+ }
97
+
98
+ return tokenData;
99
+
100
+ } catch (error) {
101
+ console.error('❌ トークン読み込みエラー:', error.message);
102
+ return null;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Portal API でトークン検証
108
+ */
109
+ static async validateToken(token) {
110
+ try {
111
+ const response = await this.makeRequest({
112
+ method: 'POST',
113
+ url: this.getApiUrl('/api/cli/verify'),
114
+ headers: {
115
+ 'Content-Type': 'application/json',
116
+ 'X-CLI-Token': token
117
+ },
118
+ body: JSON.stringify({ token: token })
119
+ });
120
+
121
+ if (response.success && response.data) {
122
+ return {
123
+ valid: true,
124
+ userInfo: {
125
+ id: response.data.userId,
126
+ name: response.data.userName,
127
+ email: response.data.userEmail,
128
+ role: response.data.userRole
129
+ }
130
+ };
131
+ } else {
132
+ return { valid: false, error: response.message };
133
+ }
134
+
135
+ } catch (error) {
136
+ return { valid: false, error: error.message };
137
+ }
138
+ }
139
+
140
+ /**
141
+ * 保存されたPortal CLIトークンを削除
142
+ */
143
+ static async deleteToken() {
144
+ try {
145
+ if (fs.existsSync(this.TOKEN_FILE_PATH)) {
146
+ fs.unlinkSync(this.TOKEN_FILE_PATH);
147
+ return true;
148
+ }
149
+ return true;
150
+ } catch (error) {
151
+ console.error('❌ トークン削除エラー:', error.message);
152
+ return false;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * トークン形式検証
158
+ */
159
+ static validateTokenFormat(token) {
160
+ if (!token || typeof token !== 'string') {
161
+ return false;
162
+ }
163
+
164
+ // Portal発行形式: cli_xxxxx_[64文字の16進数]
165
+ const patterns = [
166
+ /^cli_[a-z0-9]+_[a-f0-9]{64}$/, // 通常形式
167
+ /^cli_vscode_test_[a-f0-9]{64}$/ // テスト形式
168
+ ];
169
+
170
+ return patterns.some(pattern => pattern.test(token));
171
+ }
172
+
173
+ /**
174
+ * データ暗号化
175
+ */
176
+ static encrypt(text) {
177
+ const algorithm = 'aes-256-cbc';
178
+ const key = crypto.scryptSync(this.ENCRYPTION_KEY, 'salt', 32);
179
+ const iv = crypto.randomBytes(16);
180
+
181
+ const cipher = crypto.createCipheriv(algorithm, key, iv);
182
+ let encrypted = cipher.update(text, 'utf8', 'hex');
183
+ encrypted += cipher.final('hex');
184
+
185
+ return iv.toString('hex') + ':' + encrypted;
186
+ }
187
+
188
+ /**
189
+ * データ復号化
190
+ */
191
+ static decrypt(encryptedData) {
192
+ const algorithm = 'aes-256-cbc';
193
+ const key = crypto.scryptSync(this.ENCRYPTION_KEY, 'salt', 32);
194
+
195
+ const parts = encryptedData.split(':');
196
+ const iv = Buffer.from(parts[0], 'hex');
197
+ const encrypted = parts[1];
198
+
199
+ const decipher = crypto.createDecipheriv(algorithm, key, iv);
200
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
201
+ decrypted += decipher.final('utf8');
202
+
203
+ return decrypted;
204
+ }
205
+
206
+ /**
207
+ * HTTP/HTTPS リクエスト実行
208
+ */
209
+ static async makeRequest(options) {
210
+ return new Promise((resolve, reject) => {
211
+ const url = new URL(options.url);
212
+ const requestOptions = {
213
+ hostname: url.hostname,
214
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
215
+ path: url.pathname + url.search,
216
+ method: options.method || 'GET',
217
+ headers: options.headers || {},
218
+ timeout: this.REQUEST_TIMEOUT
219
+ };
220
+
221
+ const client = url.protocol === 'https:' ? https : http;
222
+
223
+ const req = client.request(requestOptions, (res) => {
224
+ let data = '';
225
+
226
+ res.on('data', (chunk) => {
227
+ data += chunk;
228
+ });
229
+
230
+ res.on('end', () => {
231
+ try {
232
+ const jsonData = JSON.parse(data);
233
+ resolve(jsonData);
234
+ } catch (error) {
235
+ reject(new Error(`Invalid JSON response: ${data}`));
236
+ }
237
+ });
238
+ });
239
+
240
+ req.on('error', (error) => {
241
+ reject(error);
242
+ });
243
+
244
+ req.on('timeout', () => {
245
+ req.destroy();
246
+ reject(new Error('Request timeout'));
247
+ });
248
+
249
+ if (options.body) {
250
+ req.write(options.body);
251
+ }
252
+
253
+ req.end();
254
+ });
255
+ }
256
+ }
257
+
258
+ module.exports = { TokenManager };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Portal APIからプロンプトを取得するモジュール
3
+ */
4
+
5
+ import fetch from 'node-fetch';
6
+
7
+ const PORTAL_URL = process.env.PORTAL_URL || 'https://bluelamp-6clpzmy5pa-an.a.run.app';
8
+
9
+ /**
10
+ * キーワードに基づいてPortalからプロンプトを取得
11
+ */
12
+ export async function fetchPromptFromPortal(keyword) {
13
+ try {
14
+ // Portal APIエンドポイント
15
+ const response = await fetch(`${PORTAL_URL}/api/prompts/by-keyword/${encodeURIComponent(keyword)}`, {
16
+ method: 'GET',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ }
20
+ });
21
+
22
+ if (!response.ok) {
23
+ console.error(`Portal API error: ${response.status}`);
24
+ return null;
25
+ }
26
+
27
+ const data = await response.json();
28
+
29
+ if (!data.prompt) {
30
+ console.error('No prompt found in response');
31
+ return null;
32
+ }
33
+
34
+ // Portalから取得したプロンプトデータを返す
35
+ return {
36
+ title: data.prompt.name || keyword,
37
+ content: data.prompt.text || '',
38
+ tags: data.prompt.tags || [],
39
+ level: 'expert'
40
+ };
41
+
42
+ } catch (error) {
43
+ console.error('Failed to fetch prompt from Portal:', error);
44
+ return null;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * フォールバック用のデフォルトプロンプト
50
+ */
51
+ export const DEFAULT_PROMPTS = {
52
+ 'BL-要件定義': {
53
+ title: '要件定義エンジニア',
54
+ content: `あなたは要件定義のスペシャリストです。
55
+ - ユーザーの要求を正確に理解し、技術要件に落とし込む
56
+ - 機能要件と非機能要件を明確に分離
57
+ - 実装可能性を考慮した要件定義`,
58
+ level: 'expert'
59
+ },
60
+ // ... 他の15個も同様
61
+ };
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * BlueLamp VSCode MCP Server
5
+ * VSCode拡張機能専用のMCPサーバー実装
6
+ */
7
+
8
+ const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
9
+ const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
10
+ const {
11
+ CallToolRequestSchema,
12
+ ListToolsRequestSchema,
13
+ } = require('@modelcontextprotocol/sdk/types.js');
14
+ const { getPromptFromPortal } = require('./portal-api-client.cjs');
15
+ const path = require('path');
16
+ const fs = require('fs');
17
+ const os = require('os');
18
+
19
+ // .envファイルを読み込み(.env.localを優先)
20
+ const dotenvLocalPath = path.join(__dirname, '../../.env.local');
21
+ const dotenvPath = path.join(__dirname, '../../.env');
22
+
23
+ if (fs.existsSync(dotenvLocalPath)) {
24
+ require('dotenv').config({ path: dotenvLocalPath });
25
+ console.error('[MCP Server] Loaded .env.local');
26
+ } else if (fs.existsSync(dotenvPath)) {
27
+ require('dotenv').config({ path: dotenvPath });
28
+ console.error('[MCP Server] Loaded .env');
29
+ }
30
+
31
+ // 環境変数から取得
32
+ const KEYWORD = process.env.BLUELAMP_KEYWORD || 'BL-要件定義';
33
+ const TOKEN = process.env.BLUELAMP_TOKEN;
34
+ const PORTAL_URL = process.env.PORTAL_URL || 'https://bluelamp-6clpzmy5pa-an.a.run.app';
35
+
36
+ // ログファイル設定(固定名にして見つけやすくする)
37
+ const LOG_FILE = path.join(os.tmpdir(), 'bluelamp-vscode-mcp-latest.log');
38
+
39
+ function log(message) {
40
+ const timestamp = new Date().toISOString();
41
+ const logMessage = `[${timestamp}] ${message}\n`;
42
+ fs.appendFileSync(LOG_FILE, logMessage);
43
+ console.error(`[MCP Server] ${message}`);
44
+ }
45
+
46
+ log(`MCP Server started - Log file: ${LOG_FILE}`);
47
+ log(`Environment variables:`);
48
+ log(` KEYWORD: ${KEYWORD}`);
49
+ log(` TOKEN: ${TOKEN ? TOKEN.substring(0, 20) + '...' : 'NOT SET'}`);
50
+ log(` PORTAL_URL: ${PORTAL_URL}`);
51
+ log(` .env file exists: ${fs.existsSync(dotenvPath)}`);
52
+
53
+ class BlueLampVSCodeServer {
54
+ constructor() {
55
+ this.server = new Server(
56
+ {
57
+ name: 'bluelamp-vscode',
58
+ version: '1.0.0',
59
+ },
60
+ {
61
+ capabilities: {
62
+ tools: {},
63
+ },
64
+ }
65
+ );
66
+
67
+ this.setupHandlers();
68
+ }
69
+
70
+ setupHandlers() {
71
+ // ツール一覧を返す
72
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
73
+ log('[MCP Server] ListTools request received');
74
+ return {
75
+ tools: [
76
+ {
77
+ name: 'inject_knowledge',
78
+ description: 'BlueLamp VSCode開発プロンプトの知識を注入',
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ keyword: {
83
+ type: 'string',
84
+ description: 'キーワード(@相談 など、そのまま指定してください。プレフィックスの追加は不要です)',
85
+ },
86
+ },
87
+ required: ['keyword'],
88
+ },
89
+ },
90
+ ],
91
+ };
92
+ });
93
+
94
+ // ツール実行
95
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
96
+ if (request.params.name === 'inject_knowledge') {
97
+ const { keyword } = request.params.arguments;
98
+
99
+ log(`[MCP Server] inject_knowledge called with keyword: ${keyword}`);
100
+ log(`[MCP Server] TOKEN: ${TOKEN ? TOKEN.substring(0, 20) + '...' : 'missing'}`);
101
+ log(`[MCP Server] PORTAL_URL: ${PORTAL_URL}`);
102
+
103
+ // Portal APIから認証付きで知識を取得
104
+ const result = await getPromptFromPortal(keyword, TOKEN);
105
+
106
+ if (!result || !result.success) {
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: `❌ Portal APIから知識を取得できませんでした: "${keyword}"`,
112
+ },
113
+ ],
114
+ };
115
+ }
116
+
117
+ // 知識注入メッセージを返す
118
+ return {
119
+ content: [
120
+ {
121
+ type: 'text',
122
+ text: `✅ BlueLamp VSCode 知識注入完了
123
+
124
+ ${result.knowledge}
125
+
126
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
127
+ 専門知識が注入されました。このロールで支援を開始します。`,
128
+ },
129
+ ],
130
+ };
131
+ }
132
+
133
+ return {
134
+ content: [
135
+ {
136
+ type: 'text',
137
+ text: `Unknown tool: ${request.params.name}`,
138
+ },
139
+ ],
140
+ };
141
+ });
142
+ }
143
+
144
+ async run() {
145
+ const transport = new StdioServerTransport();
146
+ await this.server.connect(transport);
147
+ console.error('BlueLamp VSCode MCP server started');
148
+
149
+ // 起動時に自動的に知識を注入(環境変数で指定されたキーワード)
150
+ if (KEYWORD && TOKEN) {
151
+ log(`[MCP Server] Auto-injecting knowledge for keyword: ${KEYWORD}`);
152
+ const result = await getPromptFromPortal(KEYWORD, TOKEN);
153
+ if (result && result.success) {
154
+ log(`[MCP Server] Auto-injection successful: ${result.title}`);
155
+ } else {
156
+ log(`[MCP Server] Auto-injection failed for keyword: ${KEYWORD}`);
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ // サーバー起動
163
+ const server = new BlueLampVSCodeServer();
164
+ server.run().catch(console.error);
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Portal API Client for BlueLamp VSCode
3
+ * 認証付きでPortal APIからプロンプトを取得
4
+ */
5
+
6
+ const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
7
+ const https = require('https');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ // 自己署名証明書を許可するHTTPSエージェント
13
+ const httpsAgent = new https.Agent({
14
+ rejectUnauthorized: false
15
+ });
16
+
17
+ const PORTAL_URL = process.env.PORTAL_URL || 'https://bluelamp-6clpzmy5pa-an.a.run.app';
18
+
19
+ // ログファイル(index.cjsと同じファイルに書き込む)
20
+ const LOG_FILE = path.join(os.tmpdir(), 'bluelamp-vscode-mcp-latest.log');
21
+
22
+ // 共通設定のキャッシュ(5分間有効)
23
+ let globalConfigCache = null;
24
+ let cacheExpiry = 0;
25
+
26
+ function log(message) {
27
+ const timestamp = new Date().toISOString();
28
+ const logMessage = `[${timestamp}] ${message}\n`;
29
+ fs.appendFileSync(LOG_FILE, logMessage);
30
+ console.error(message);
31
+ }
32
+
33
+ /**
34
+ * 共通CLAUDE.MD設定を取得(オプショナル)
35
+ * エラーが発生してもnullを返して処理を継続
36
+ */
37
+ async function getGlobalConfig(token) {
38
+ try {
39
+ // キャッシュが有効ならそれを返す
40
+ if (globalConfigCache && Date.now() < cacheExpiry) {
41
+ log('[portal-api-client] Using cached global config');
42
+ return globalConfigCache;
43
+ }
44
+
45
+ if (!token) {
46
+ return null;
47
+ }
48
+
49
+ // Portal APIから共通設定を取得
50
+ const url = `${PORTAL_URL}/api/cli/global-config`;
51
+ log(`[portal-api-client] Fetching global config from: ${url}`);
52
+
53
+ const response = await fetch(url, {
54
+ method: 'GET',
55
+ headers: {
56
+ 'X-CLI-Token': token,
57
+ 'Content-Type': 'application/json',
58
+ 'User-Agent': 'BlueLamp-VSCode-MCP/1.0'
59
+ },
60
+ agent: httpsAgent
61
+ });
62
+
63
+ if (response.ok) {
64
+ const data = await response.json();
65
+ if (data.success && data.content) {
66
+ log('[portal-api-client] Global config fetched successfully');
67
+ globalConfigCache = data.content;
68
+ cacheExpiry = Date.now() + 300000; // 5分間キャッシュ
69
+ return data.content;
70
+ }
71
+ }
72
+
73
+ log('[portal-api-client] Global config not available (optional)');
74
+ return null;
75
+
76
+ } catch (error) {
77
+ // エラーが発生しても処理を継続(後方互換性)
78
+ log(`[portal-api-client] Global config fetch error (optional): ${error.message}`);
79
+ return null;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Portal APIから認証付きでプロンプトを取得
85
+ */
86
+ async function getPromptFromPortal(keyword, token) {
87
+ try {
88
+ log(`[portal-api-client] getPromptFromPortal called with keyword: ${keyword}, token: ${token ? 'present' : 'missing'}`);
89
+
90
+ if (!token) {
91
+ log('[portal-api-client] No authentication token provided');
92
+ return null;
93
+ }
94
+
95
+ // キーワードをそのまま使用
96
+ const encodedKeyword = encodeURIComponent(keyword);
97
+ const url = `${PORTAL_URL}/api/cli/prompts/knowledge-injection/${encodedKeyword}`;
98
+ log(`[portal-api-client] Direct keyword search: ${url}`);
99
+ log(`[portal-api-client] Token (first 30 chars): ${token.substring(0, 30)}...`);
100
+
101
+ log(`[portal-api-client] About to call fetch...`);
102
+ const response = await fetch(url, {
103
+ method: 'GET',
104
+ headers: {
105
+ 'X-CLI-Token': token,
106
+ 'Content-Type': 'application/json',
107
+ 'User-Agent': 'BlueLamp-VSCode-MCP/1.0'
108
+ },
109
+ agent: url.startsWith('https') ? httpsAgent : undefined
110
+ });
111
+ log(`[portal-api-client] Fetch completed`);
112
+
113
+ log(`[portal-api-client] Response status: ${response.status}`);
114
+ log(`[portal-api-client] Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
115
+
116
+ if (!response.ok) {
117
+ log(`[portal-api-client] Portal API error: ${response.status}`);
118
+ const errorText = await response.text();
119
+ log(`[portal-api-client] Error response: ${errorText}`);
120
+ return null;
121
+ }
122
+
123
+ const data = await response.json();
124
+ log(`[portal-api-client] Direct keyword search completed successfully`);
125
+
126
+ if (!data.success) {
127
+ log(`[portal-api-client] API returned success: false`);
128
+ return null;
129
+ }
130
+
131
+ // 直接プロンプトデータを取得(検索不要)
132
+ const prompt = data.data;
133
+
134
+ if (!prompt) {
135
+ log(`[portal-api-client] No prompt found for keyword: ${keyword}`);
136
+ return null;
137
+ }
138
+
139
+ log(`[portal-api-client] Found prompt: ${prompt.title}`);
140
+
141
+ // プロンプトの詳細情報が既に含まれているかチェック
142
+ if (prompt.content || prompt.text) {
143
+ // 詳細情報が含まれている場合はそのまま返す
144
+ return {
145
+ success: true,
146
+ knowledge: prompt.content || prompt.text,
147
+ title: prompt.title,
148
+ tags: prompt.tags || []
149
+ };
150
+ }
151
+
152
+ // 個別のプロンプト詳細を取得
153
+ try {
154
+ const detailUrl = `${PORTAL_URL}/api/cli/prompts/${prompt.id}`;
155
+ log(`[portal-api-client] Fetching prompt details from: ${detailUrl}`);
156
+
157
+ const detailResponse = await fetch(detailUrl, {
158
+ method: 'GET',
159
+ headers: {
160
+ 'X-CLI-Token': token,
161
+ 'Content-Type': 'application/json',
162
+ 'User-Agent': 'BlueLamp-VSCode-MCP/1.0'
163
+ },
164
+ agent: httpsAgent
165
+ });
166
+
167
+ if (detailResponse.ok) {
168
+ const detailData = await detailResponse.json();
169
+ if (detailData.success && detailData.data?.prompt?.content) {
170
+ log(`[portal-api-client] Successfully fetched prompt content`);
171
+
172
+ // 共通設定を取得(オプショナル)
173
+ const globalConfig = await getGlobalConfig(token);
174
+ let finalContent = detailData.data.prompt.content;
175
+
176
+ // 共通設定があれば先頭に追加
177
+ if (globalConfig) {
178
+ log(`[portal-api-client] Adding global config to prompt content`);
179
+ finalContent = `${globalConfig}\n\n---\n\n${finalContent}`;
180
+ }
181
+
182
+ return {
183
+ success: true,
184
+ knowledge: finalContent,
185
+ title: prompt.title || prompt.name,
186
+ tags: prompt.tags
187
+ };
188
+ }
189
+ }
190
+ } catch (error) {
191
+ log(`[portal-api-client] Error fetching prompt details: ${error.message}`);
192
+ }
193
+
194
+ // フォールバック: リスト情報のみ返す
195
+ return {
196
+ success: true,
197
+ knowledge: prompt.description || `# ${prompt.title}\n\n詳細情報の取得に失敗しました。`,
198
+ title: prompt.title || prompt.name,
199
+ tags: prompt.tags
200
+ };
201
+
202
+ } catch (error) {
203
+ log(`[portal-api-client] FATAL ERROR in getPromptFromPortal: ${error.message}`);
204
+ log(`[portal-api-client] Error stack: ${error.stack}`);
205
+ console.error('Failed to get prompt from Portal:', error);
206
+ return null;
207
+ }
208
+ }
209
+
210
+ module.exports = { getPromptFromPortal, getGlobalConfig };