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,106 @@
1
+ ---
2
+ name: ブルーランプ
3
+ description: パフォーマーエージェント - オーケストレーターの指示に応じて適切な役割・ツールを選択・実行
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # ブルーランプエージェント
9
+
10
+
11
+ 1. **初期化**:オーケストレーターから指示があった場合は、inject_knowledge(keyword: "キーワード")こちらで指示通りの知識を注入する(指示がなければ不要)
12
+ 2. **オーケストレーター指示の確認**: タスク指示を解析し、実行内容を特定
13
+ 3. **タスク実行**: 注入された専門知識に基づいて適切な処理を実行
14
+
15
+ ## 🌟 基本理念
16
+ 「シンプルさは究極の洗練である」- レオナルド・ダ・ヴィンチ
17
+ 「プロジェクトでいまここに不要なコードは一文字も残さない」
18
+ 「最高の仕様書はドキュメントではなく美しく整ったアーキテクチャのコードそのものである」
19
+ 「秩序を保つことに責任を持つ。整理整頓された綺麗な部屋のようにコードやドキュメントの片付け整理を忘れない」
20
+ 「ジョブスのように誰もみてない細部のコードの美しさにも職人のようにこだわる」
21
+ ## 🎯 5つの核心原則
22
+
23
+ ### 1. 最小性の原則(Principle of Minimalism)
24
+ **「必要最小限を超えない」**
25
+ - 新規作成より既存活用を優先する
26
+ - 解決策は常に最もシンプルなものを選ぶ
27
+ - あったらいいなはないほうがいい
28
+
29
+ ### 2. 単一性の原則(Principle of Singularity)
30
+ **「真実の源は常に一つ」**
31
+ - SQLAlchemyモデルはbackend/app/models/__init__.pyに集約(DB定義の単一源)
32
+ - 実装計画はSCOPE_PROGRESS.mdに集約させる
33
+ - 仕様書やロジックなどはrequirements.mdに集約させる
34
+
35
+ ### 3. 刹那性の原則(Principle of Ephemerality)
36
+ **「今、ここで必要なものだけを残す」**
37
+ - 役目を終えたものは即座に削除する
38
+ - 修正や変更により使わなくなったコードは即リファクタリング
39
+ - 後方互換の原則禁止(ただし修正時は安全を確保するよう慎重に)
40
+ - 未来の「もしかして」のために保持しない
41
+ - 生きているコードとドキュメントのみを維持する
42
+
43
+ ### 4. 実証性の原則(Principle of Evidence)
44
+ **「事実こそが真実への唯一の道」**
45
+ - 環境変数やデータベースを積極的に確認する。
46
+ - 開発環境でもバックエンドは全て本番のAPIを使って検証テスト作成する。バックエンドのモック絶対禁止。
47
+ - デバッグで明確に原因がわからない場合はログを設置する。推測しない。
48
+ - 外部APIや比較的新しい技術はweb検索を積極的に活用する。仕様を推測しない。
49
+ - **Web検索前に必ず`date`コマンドで現在日時を確認し、現在の最新情報を検索する**
50
+
51
+ ### 5. 潔癖性の原則(Principle of Fail-Fast)
52
+ **「失敗は隠すな、早く明確に、透明に失敗せよ」**
53
+ - エラーは即座に表面化させる
54
+ - フォールバックによる問題の隠蔽を禁止する
55
+ - そもそもフォールバック不要のシンプルで堅牢性の高い構造を作る
56
+
57
+ ## ドキュメント管理の原則
58
+
59
+ 単一の真実源の原則を守らせ、勝手なドキュメントを無作為に作らせないように制御してください。
60
+ - **docs/SCOPE_PROGRESS.md**: 今これからやるべき実装の計画
61
+ - **docs/requirements.md**: 今これからやるべき実装の詳細
62
+ - **docs/DEPLOYMENT.md**: デプロイ情報
63
+ - **docs/designsystem.md**: デザインシステム
64
+ - **docs/api-specs/[ページ名]-api.md**: API仕様書
65
+
66
+ 例えばリサーチして何か知見があり、その結果としてドキュメント化したいのであれば
67
+ requirements.md(要件やロジック)
68
+ SCOPE_PROGRESS.md(実装計画)
69
+
70
+ こちらにのみ記載可能であり、こちらに記載する価値がなければ回答のみで報告してください。
71
+ これ以外のドキュメントはユーザーへの確認と明確な許諾や指示がない限り作成してはいけません。
72
+
73
+ requirements.md(要件やロジック)
74
+ SCOPE_PROGRESS.md(実装計画)
75
+
76
+ こちらは実装が完了しコードに落とし込まれている記載は積極的に削除し不要な記載はなくす。
77
+
78
+ ## 🔧 FastAPI + SQLAlchemy開発の絶対原則
79
+
80
+ ### 6. 環境変数の絶対原則
81
+ - **開発中は全ての場面で.env.localのみ使用**
82
+ - テストスクリプトは必ず.env.localを読み込ませる
83
+ - .env.test、.env.developmentなど作らない
84
+ - DATABASE_URLは.env.localの1箇所のみ
85
+
86
+ ### 7. SQLAlchemy/DBの絶対原則
87
+ - **SQLAlchemyエラーが出たら`alembic upgrade head`を実行**
88
+ - スキーマ変更は`alembic revision --autogenerate`後に`alembic upgrade head`
89
+ - SQLAlchemyモデルはbackend/app/models/__init__.pyに集約
90
+ - **DBURLエラー時は環境変数エラーとして即報告**
91
+
92
+ ### 8. テストも.env.localを使用
93
+ - テスト専用DBは作らない
94
+ - 同じDBで問題なし(開発段階)
95
+ - process.env.DATABASE_URLをそのまま使用
96
+
97
+ ### 9. サーバー起動の絶対原則
98
+ - サーバーは1つ維持
99
+ - 再起動禁止(ホットリロードを使う)
100
+ - 既存サーバーがある場合はそれを使う
101
+ (理由:複数エージェントで同プロジェクトを扱うため、既存サーバーのほとんどは同じプロジェクト。失敗時のみ調査し、違う場合のみ新規起動)
102
+
103
+ ### 10. エラー時の絶対原則
104
+ - **環境変数エラー** → 全タスク強制終了し即座に報告(試行錯誤禁止)
105
+ - **SQLAlchemy接続エラー** → `alembic upgrade head`後、1回だけ再試行
106
+ - **3回同じエラー** → Web検索を許可
@@ -0,0 +1,243 @@
1
+ /**
2
+ * InteractiveLogin - 対話的ログイン機能
3
+ * CommonJS版(bluelamp-vscode用)
4
+ */
5
+
6
+ const readline = require('readline');
7
+ const { PortalAuthClient } = require('./PortalAuthClient');
8
+ const { TokenManager } = require('./TokenManager');
9
+
10
+ class InteractiveLogin {
11
+ /**
12
+ * 対話的ログインのメインフロー(トークンベース認証)
13
+ */
14
+ static async performLogin() {
15
+ try {
16
+ // Phase 1: 保存されたCLIトークンをチェック
17
+ console.log('🔵 BlueLamp VSCode - Portal認証');
18
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
19
+
20
+ const savedTokenData = await TokenManager.loadToken();
21
+
22
+ if (savedTokenData && savedTokenData.token) {
23
+ console.log('🔍 保存されたトークンを確認中...');
24
+
25
+ // トークン検証
26
+ const validation = await TokenManager.validateToken(savedTokenData.token);
27
+ console.log(' 検証結果:', validation);
28
+
29
+ if (validation.valid) {
30
+ console.log('🔷 既存のトークンが有効です');
31
+ return {
32
+ token: savedTokenData.token,
33
+ user: validation.userInfo,
34
+ isFromCache: true
35
+ };
36
+ } else {
37
+ console.log('🟡 保存されたトークンが無効です');
38
+ // 無効なトークンを削除
39
+ await TokenManager.deleteToken();
40
+ }
41
+ }
42
+
43
+ // Phase 2: CLIトークンが無効/存在しない → 認証フロー
44
+ console.log('');
45
+ console.log('📝 Portalログイン情報を入力してください');
46
+ console.log('');
47
+
48
+ // 対話的ログイン情報入力
49
+ const credentials = await this.promptCredentials();
50
+
51
+ // Portal API認証
52
+ const authResult = await PortalAuthClient.authenticate(
53
+ credentials.email,
54
+ credentials.password
55
+ );
56
+
57
+ if (!authResult.success) {
58
+ // エラーメッセージの処理
59
+ if (authResult.error && authResult.error.includes('ナレッジインジェクション使用回数上限')) {
60
+ console.error('');
61
+ console.error('❌ ナレッジインジェクション使用回数が上限に達しています');
62
+ console.error(' Portalで使用状況を確認してください');
63
+ } else {
64
+ console.error('❌ 認証エラー:', authResult.error);
65
+ }
66
+ process.exit(1);
67
+ }
68
+
69
+ // Phase 3: CLIトークンを保存
70
+ console.log('💾 トークンを保存中...');
71
+ const tokenSaved = await TokenManager.saveToken(authResult.token, authResult.user);
72
+
73
+ if (!tokenSaved) {
74
+ console.warn('⚠️ トークン保存に失敗しました(認証は成功)');
75
+ } else {
76
+ console.log('✅ トークンを安全に保存しました');
77
+ }
78
+
79
+ // Phase 4: .envファイルのトークンも更新
80
+ await this.updateEnvToken(authResult.token);
81
+
82
+ return {
83
+ token: authResult.token,
84
+ user: authResult.user,
85
+ isFirstTime: true,
86
+ isFromCache: false
87
+ };
88
+
89
+ } catch (error) {
90
+ console.error('❌ 予期しないエラー:', error.message);
91
+ process.exit(1);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * ログイン情報の対話的入力
97
+ */
98
+ static async promptCredentials() {
99
+ const rl = readline.createInterface({
100
+ input: process.stdin,
101
+ output: process.stdout
102
+ });
103
+
104
+ try {
105
+ const email = await this.question(rl, 'Email: ');
106
+
107
+ if (!this.validateEmail(email)) {
108
+ console.error('❌ 無効なメールアドレス形式です');
109
+ rl.close();
110
+ process.exit(1);
111
+ }
112
+
113
+ const password = await this.question(rl, 'Password: ');
114
+
115
+ if (!password || password.trim().length === 0) {
116
+ console.error('❌ パスワードが入力されていません');
117
+ rl.close();
118
+ process.exit(1);
119
+ }
120
+
121
+ rl.close();
122
+
123
+ return {
124
+ email: email.trim(),
125
+ password: password.trim()
126
+ };
127
+
128
+ } catch (error) {
129
+ rl.close();
130
+ throw error;
131
+ }
132
+ }
133
+
134
+ /**
135
+ * readline.question のPromise版
136
+ */
137
+ static question(rl, prompt) {
138
+ return new Promise((resolve) => {
139
+ rl.question(prompt, (answer) => {
140
+ resolve(answer);
141
+ });
142
+ });
143
+ }
144
+
145
+ /**
146
+ * メールアドレスの簡易バリデーション
147
+ */
148
+ static validateEmail(email) {
149
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
150
+ return emailRegex.test(email);
151
+ }
152
+
153
+ /**
154
+ * ログイン成功時の情報表示
155
+ */
156
+ static displayLoginSuccess(result) {
157
+ console.log('');
158
+ console.log('✅ Portal認証成功');
159
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
160
+
161
+ if (result.user) {
162
+ console.log(`👤 ユーザー: ${result.user.name || result.user.email}`);
163
+ console.log(`📧 Email: ${result.user.email}`);
164
+ if (result.user.role) {
165
+ console.log(`🎭 ロール: ${result.user.role}`);
166
+ }
167
+ }
168
+
169
+ if (result.isFromCache) {
170
+ console.log('📦 保存済みトークンを使用');
171
+ } else if (result.isFirstTime) {
172
+ console.log('🎉 初回ログイン完了');
173
+ }
174
+
175
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
176
+ console.log('');
177
+ }
178
+
179
+ /**
180
+ * ログアウト機能
181
+ */
182
+ static async logout() {
183
+ console.log('🔵 BlueLamp VSCode - ログアウト');
184
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
185
+
186
+ const rl = readline.createInterface({
187
+ input: process.stdin,
188
+ output: process.stdout
189
+ });
190
+
191
+ try {
192
+ const confirm = await this.question(rl, 'ログイン情報を削除しますか? (y/N): ');
193
+
194
+ if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
195
+ const deleted = await TokenManager.deleteToken();
196
+
197
+ if (deleted) {
198
+ console.log('✅ ログイン情報を削除しました');
199
+ } else {
200
+ console.log('⚠️ ログイン情報が見つかりません');
201
+ }
202
+ } else {
203
+ console.log('❌ キャンセルしました');
204
+ }
205
+
206
+ } finally {
207
+ rl.close();
208
+ }
209
+ }
210
+
211
+ /**
212
+ * .envファイルのトークンを更新
213
+ */
214
+ static async updateEnvToken(newToken) {
215
+ const fs = require('fs');
216
+ const path = require('path');
217
+
218
+ try {
219
+ const envPath = path.join(__dirname, '../../.env');
220
+
221
+ if (fs.existsSync(envPath)) {
222
+ let envContent = fs.readFileSync(envPath, 'utf8');
223
+
224
+ // BLUELAMP_TOKENの行を更新
225
+ if (envContent.includes('BLUELAMP_TOKEN=')) {
226
+ envContent = envContent.replace(
227
+ /BLUELAMP_TOKEN=.*/,
228
+ `BLUELAMP_TOKEN=${newToken}`
229
+ );
230
+ } else {
231
+ // BLUELAMP_TOKENの行が存在しない場合は追加
232
+ envContent += `\nBLUELAMP_TOKEN=${newToken}`;
233
+ }
234
+
235
+ fs.writeFileSync(envPath, envContent);
236
+ }
237
+ } catch (error) {
238
+ console.warn('⚠️ .envファイルの更新に失敗:', error.message);
239
+ }
240
+ }
241
+ }
242
+
243
+ module.exports = { InteractiveLogin };
@@ -0,0 +1,187 @@
1
+ /**
2
+ * PortalAuthClient - Portal API連携クライアント
3
+ * CommonJS版(bluelamp-vscode用)
4
+ */
5
+
6
+ const os = require('os');
7
+ const https = require('https');
8
+ const http = require('http');
9
+ const { URL } = require('url');
10
+
11
+ class PortalAuthClient {
12
+ static PORTAL_API_URL_PROD = 'https://bluelamp-6clpzmy5pa-an.a.run.app/api/cli/login';
13
+ static PORTAL_API_URL_LOCAL = 'https://bluelamp-6clpzmy5pa-an.a.run.app/api/cli/login';
14
+ static MAX_RETRIES = 3;
15
+ static RETRY_DELAY = 1000; // 1秒
16
+ static REQUEST_TIMEOUT = 10000; // 10秒
17
+
18
+ /**
19
+ * 開発環境かどうかを判定
20
+ */
21
+ static isLocalDev() {
22
+ return process.env.PORTAL_URL && process.env.PORTAL_URL.includes('localhost');
23
+ }
24
+
25
+ /**
26
+ * APIエンドポイントを取得
27
+ */
28
+ static getApiUrl() {
29
+ return this.isLocalDev() ? this.PORTAL_API_URL_LOCAL : this.PORTAL_API_URL_PROD;
30
+ }
31
+
32
+ /**
33
+ * Portal API認証・CLIトークン取得
34
+ */
35
+ static async authenticate(email, password) {
36
+ const deviceInfo = this.generateDeviceInfo();
37
+ const authRequest = {
38
+ email: email,
39
+ password: password,
40
+ deviceInfo: deviceInfo
41
+ };
42
+
43
+ let lastError = null;
44
+
45
+ for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) {
46
+ try {
47
+ console.log(`🔐 認証試行 ${attempt}/${this.MAX_RETRIES}...`);
48
+ const response = await this.makeRequest(authRequest);
49
+
50
+ if (response.success) {
51
+ console.log('✅ Portal認証成功');
52
+ console.log('📝 トークン取得成功');
53
+ return {
54
+ success: true,
55
+ token: response.data.token,
56
+ user: {
57
+ name: response.data.userName,
58
+ role: response.data.userRole,
59
+ email: email
60
+ }
61
+ };
62
+ } else {
63
+ throw new Error(response.message || 'Portal認証に失敗しました');
64
+ }
65
+
66
+ } catch (error) {
67
+ lastError = error;
68
+ console.error(`❌ 認証エラー: ${error.message}`);
69
+ if (attempt < this.MAX_RETRIES) {
70
+ console.log(`⏳ ${this.RETRY_DELAY}ms後にリトライ...`);
71
+ await this.sleep(this.RETRY_DELAY);
72
+ }
73
+ }
74
+ }
75
+
76
+ return {
77
+ success: false,
78
+ error: lastError?.message || 'Portal認証に失敗しました'
79
+ };
80
+ }
81
+
82
+ /**
83
+ * デバイス情報の自動生成
84
+ */
85
+ static generateDeviceInfo() {
86
+ return {
87
+ deviceName: os.hostname(),
88
+ platform: os.platform(),
89
+ arch: os.arch(),
90
+ userAgent: 'BlueLamp VSCode Extension',
91
+ nodeVersion: process.version,
92
+ timestamp: Date.now()
93
+ };
94
+ }
95
+
96
+ /**
97
+ * HTTP/HTTPS リクエストの実行
98
+ */
99
+ static async makeRequest(authRequest) {
100
+ return new Promise((resolve, reject) => {
101
+ const url = new URL(this.getApiUrl());
102
+ const postData = JSON.stringify(authRequest);
103
+
104
+ const options = {
105
+ hostname: url.hostname,
106
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
107
+ path: url.pathname,
108
+ method: 'POST',
109
+ headers: {
110
+ 'Content-Type': 'application/json',
111
+ 'Content-Length': Buffer.byteLength(postData),
112
+ 'User-Agent': 'BlueLamp VSCode Extension',
113
+ 'Accept': 'application/json'
114
+ },
115
+ timeout: this.REQUEST_TIMEOUT,
116
+ rejectUnauthorized: false // 自己署名証明書を許可
117
+ };
118
+
119
+ const client = url.protocol === 'https:' ? https : http;
120
+
121
+ const req = client.request(options, (res) => {
122
+ let data = '';
123
+
124
+ res.on('data', (chunk) => {
125
+ data += chunk;
126
+ });
127
+
128
+ res.on('end', () => {
129
+ try {
130
+ let response;
131
+ try {
132
+ response = JSON.parse(data);
133
+ } catch (e) {
134
+ if (res.statusCode !== 200) {
135
+ reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
136
+ } else {
137
+ reject(new Error(`レスポンス解析エラー: ${e.message}`));
138
+ }
139
+ return;
140
+ }
141
+
142
+ // ステータスコードが200以外の場合
143
+ if (res.statusCode !== 200) {
144
+ if (response && response.message) {
145
+ // 使用回数制限エラーの場合
146
+ if (response.error === 'KNOWLEDGE_INJECTION_LIMIT_EXCEEDED') {
147
+ reject(new Error(response.message));
148
+ } else {
149
+ reject(new Error(response.message));
150
+ }
151
+ } else {
152
+ reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
153
+ }
154
+ return;
155
+ }
156
+
157
+ resolve(response);
158
+
159
+ } catch (error) {
160
+ reject(new Error(`レスポンス解析エラー: ${error.message}`));
161
+ }
162
+ });
163
+ });
164
+
165
+ req.on('error', (error) => {
166
+ reject(new Error(`ネットワークエラー: ${error.message}`));
167
+ });
168
+
169
+ req.on('timeout', () => {
170
+ req.destroy();
171
+ reject(new Error('リクエストタイムアウト'));
172
+ });
173
+
174
+ req.write(postData);
175
+ req.end();
176
+ });
177
+ }
178
+
179
+ /**
180
+ * スリープ関数
181
+ */
182
+ static sleep(ms) {
183
+ return new Promise(resolve => setTimeout(resolve, ms));
184
+ }
185
+ }
186
+
187
+ module.exports = { PortalAuthClient };