bluelamp 6.6.10 → 6.6.12
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/dist/auth/InteractiveLogin.js +1 -1
- package/dist/auth/PortalAuthClient.js +1 -1
- package/dist/auth/TokenManager.js +1 -1
- package/dist/bin/bluelamp-vscode-base.mjs +1 -1
- package/dist/lib/connectResilience.d.ts +43 -0
- package/dist/lib/connectResilience.d.ts.map +1 -0
- package/dist/lib/connectResilience.js +1 -0
- package/dist/lib/connectResilience.js.map +1 -0
- package/dist/lib/headlessLaunch.d.ts +27 -0
- package/dist/lib/headlessLaunch.d.ts.map +1 -1
- package/dist/lib/headlessLaunch.js +1 -1
- package/dist/lib/headlessLaunch.js.map +1 -1
- package/dist/lib/mcp-server/index.cjs +1 -1
- package/dist/lib/mcp-server/install-package.cjs +1 -1
- package/dist/lib/mcp-server/portal-api-client.cjs +1 -1
- package/dist/lib/sysPromptHandoff.d.ts +9 -0
- package/dist/lib/sysPromptHandoff.d.ts.map +1 -0
- package/dist/lib/sysPromptHandoff.js +1 -0
- package/dist/lib/sysPromptHandoff.js.map +1 -0
- package/package.json +4 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 負荷下でも portal へつながるための、接続まわりの耐性(箱 n=1959)。
|
|
3
|
+
*
|
|
4
|
+
* 🩸 塞いでいる穴(2026-09-23 実測・ami機 node v24.5.0):
|
|
5
|
+
* node 20+ の既定は `autoSelectFamily=true`・`autoSelectFamilyAttemptTimeout=250ms`。
|
|
6
|
+
* api.motohi.jp は A と AAAA を返し、IPv6 の経路が無い機体では IPv4 の 1 本だけが頼りになる。
|
|
7
|
+
* CPU 負荷で event loop が 250ms 以上止まると、**その IPv4 の試行がタイマーで打ち切られ**、
|
|
8
|
+
* IPv6 は EHOSTUNREACH で落ちて `AggregateError ETIMEDOUT`(**message が空**)になる。
|
|
9
|
+
* ⇒ 起動子は slot 設定を取れず `process.exit(1)` し、tmux session ごと消えていた。
|
|
10
|
+
* gateway は目録を取れず「Starting with no services」で立ち、本体プロンプトも載らなかった。
|
|
11
|
+
* 🔬 再現: DNS 解決の直後に loop を 400ms 塞ぐと 0/5・塞がないと 5/5(tests/connect-resilience.test.mjs)。
|
|
12
|
+
*
|
|
13
|
+
* 🔑 直し方は「試行の打ち切りを負荷の揺れより長くする」。`autoSelectFamily` 自体は切らない
|
|
14
|
+
* ——切ると IPv6 が先に返る網で IPv4 へ落ちられなくなる(直したい障害の裏返しを作る)。
|
|
15
|
+
*
|
|
16
|
+
* 2 本目の防御として、起動の前に失敗すると致命な呼び出しだけ「通信の失敗」に限って撃ち直す
|
|
17
|
+
* (判断と間隔はここの純関数・撃つのは呼び出し側)。404 / 403 のような**答えが返った失敗**は撃ち直さない。
|
|
18
|
+
*/
|
|
19
|
+
/** 1 回の接続試行を打ち切るまでの時間(ms)。既定 250ms では負荷の揺れに負ける。 */
|
|
20
|
+
export declare const CONNECT_ATTEMPT_TIMEOUT_MS = 5000;
|
|
21
|
+
/** 撃ち直しを含めた最大の試行回数(初回を含む)。 */
|
|
22
|
+
export declare const NETWORK_RETRY_ATTEMPTS = 3;
|
|
23
|
+
/** n 回目の失敗のあとに待つ時間(ms)。長さ = NETWORK_RETRY_ATTEMPTS - 1。 */
|
|
24
|
+
export declare const NETWORK_RETRY_DELAYS_MS: readonly number[];
|
|
25
|
+
/** `node:net` のうち、ここで使う口だけ。 */
|
|
26
|
+
export interface NetDefaultsLike {
|
|
27
|
+
setDefaultAutoSelectFamilyAttemptTimeout?: (value: number) => void;
|
|
28
|
+
getDefaultAutoSelectFamilyAttemptTimeout?: () => number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* プロセス全体の接続試行の打ち切りを伸ばす。**最初の socket より前に**呼ぶこと。
|
|
32
|
+
* API が無い node(< 20)では何もしない(その版は happy eyeballs を持たない=この穴も無い)。
|
|
33
|
+
* @returns 適用後の値。適用できなかったときは null
|
|
34
|
+
*/
|
|
35
|
+
export declare function applyConnectResilience(net: NetDefaultsLike): number | null;
|
|
36
|
+
/**
|
|
37
|
+
* 通信の失敗に限って撃ち直す。`isNetworkFailure` が偽の結果(成功・404 等)はそのまま返す。
|
|
38
|
+
* @param attempt 1 回撃つ関数
|
|
39
|
+
* @param isNetworkFailure 撃ち直すべき結果か
|
|
40
|
+
* @param sleep 待ち(テストから差し替える)
|
|
41
|
+
*/
|
|
42
|
+
export declare function retryOnNetworkFailure<T>(attempt: () => Promise<T>, isNetworkFailure: (result: T) => boolean, sleep?: (ms: number) => Promise<void>): Promise<T>;
|
|
43
|
+
//# sourceMappingURL=connectResilience.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectResilience.d.ts","sourceRoot":"","sources":["../../src/lib/connectResilience.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,mDAAmD;AACnD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,8BAA8B;AAC9B,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,2DAA2D;AAC3D,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAAiB,CAAC;AAEvE,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B,wCAAwC,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,wCAAwC,CAAC,EAAE,MAAM,MAAM,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAa1E;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC3C,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,EACxC,KAAK,GAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAiD,GACnF,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function applyConnectResilience(e){return"function"!=typeof e.setDefaultAutoSelectFamilyAttemptTimeout?null:(("function"==typeof e.getDefaultAutoSelectFamilyAttemptTimeout?e.getDefaultAutoSelectFamilyAttemptTimeout():0)<exports.CONNECT_ATTEMPT_TIMEOUT_MS&&e.setDefaultAutoSelectFamilyAttemptTimeout(exports.CONNECT_ATTEMPT_TIMEOUT_MS),"function"==typeof e.getDefaultAutoSelectFamilyAttemptTimeout?e.getDefaultAutoSelectFamilyAttemptTimeout():exports.CONNECT_ATTEMPT_TIMEOUT_MS)}async function retryOnNetworkFailure(e,t,T=e=>new Promise(t=>setTimeout(t,e))){let o=await e();for(let E=1;E<exports.NETWORK_RETRY_ATTEMPTS&&t(o);E++)await T(exports.NETWORK_RETRY_DELAYS_MS[E-1]??exports.NETWORK_RETRY_DELAYS_MS[exports.NETWORK_RETRY_DELAYS_MS.length-1]),o=await e();return o}Object.defineProperty(exports,"__esModule",{value:!0}),exports.NETWORK_RETRY_DELAYS_MS=exports.NETWORK_RETRY_ATTEMPTS=exports.CONNECT_ATTEMPT_TIMEOUT_MS=void 0,exports.applyConnectResilience=applyConnectResilience,exports.retryOnNetworkFailure=retryOnNetworkFailure,exports.CONNECT_ATTEMPT_TIMEOUT_MS=5e3,exports.NETWORK_RETRY_ATTEMPTS=3,exports.NETWORK_RETRY_DELAYS_MS=[1e3,2e3];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectResilience.js","sourceRoot":"","sources":["../../src/lib/connectResilience.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAsBH,wDAaC;AAQD,sDAWC;AApDD,mDAAmD;AACtC,QAAA,0BAA0B,GAAG,IAAI,CAAC;AAE/C,8BAA8B;AACjB,QAAA,sBAAsB,GAAG,CAAC,CAAC;AAExC,2DAA2D;AAC9C,QAAA,uBAAuB,GAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAQvE;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,GAAoB;IACzD,IAAI,OAAO,GAAG,CAAC,wCAAwC,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACpF,MAAM,OAAO,GACX,OAAO,GAAG,CAAC,wCAAwC,KAAK,UAAU;QAChE,CAAC,CAAC,GAAG,CAAC,wCAAwC,EAAE;QAChD,CAAC,CAAC,CAAC,CAAC;IACR,wBAAwB;IACxB,IAAI,OAAO,GAAG,kCAA0B,EAAE,CAAC;QACzC,GAAG,CAAC,wCAAwC,CAAC,kCAA0B,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,OAAO,GAAG,CAAC,wCAAwC,KAAK,UAAU;QACvE,CAAC,CAAC,GAAG,CAAC,wCAAwC,EAAE;QAChD,CAAC,CAAC,kCAA0B,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,qBAAqB,CACzC,OAAyB,EACzB,gBAAwC,EACxC,QAAuC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEpF,IAAI,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,8BAAsB,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5E,MAAM,KAAK,CAAC,+BAAuB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,+BAAuB,CAAC,+BAAuB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3G,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -25,6 +25,13 @@
|
|
|
25
25
|
* launcher が読んだうえで**自前の一時 config を組み直して**渡している。素通ししてしまうと
|
|
26
26
|
* claude 側で bluelamp サーバー定義が二重になり、#432 で直したテナント引き継ぎを壊す。
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* 箱 n=1984: 無人の起動元(scheduler)が、席の最初の prompt そのものとして task を渡す flag。
|
|
30
|
+
* 背景(2026-09-20 実測): scheduler は席が立ってから tmux で task を打ち込んでいたが、その前に席が
|
|
31
|
+
* 「本体の定義に従い開始してください」で自走し、別の便の手順を撃って閉じた(task は 1 度も届かなかった)。
|
|
32
|
+
* ⇒ task を起動引数にして、最初の prompt を task にする=届く前に動く隙間を無くす。
|
|
33
|
+
*/
|
|
34
|
+
export declare const TASK_FILE_FLAG = "--task-file";
|
|
28
35
|
/**
|
|
29
36
|
* 利用者がヘッドレス起動を要求しているか。
|
|
30
37
|
* ⚠️ 判定は `-p` / `--print` の**現れ方**だけで行い、値は見ない
|
|
@@ -60,4 +67,24 @@ export interface BuildClaudeArgsResult {
|
|
|
60
67
|
* ⇒ 挨拶定型が無いので、利用者の `-p "<指示>"` が唯一の prompt になる。
|
|
61
68
|
*/
|
|
62
69
|
export declare function buildClaudeArgs(input: BuildClaudeArgsInput): BuildClaudeArgsResult;
|
|
70
|
+
export type LauncherTask =
|
|
71
|
+
/** `--task-file` 無し=従来どおり(挨拶定型で開始) */
|
|
72
|
+
{
|
|
73
|
+
kind: 'none';
|
|
74
|
+
}
|
|
75
|
+
/** task を最初の prompt にする */
|
|
76
|
+
| {
|
|
77
|
+
kind: 'task';
|
|
78
|
+
text: string;
|
|
79
|
+
}
|
|
80
|
+
/** 指定されたのに読めない/空=**起動しない**(task の無い席を自走させない) */
|
|
81
|
+
| {
|
|
82
|
+
kind: 'invalid';
|
|
83
|
+
error: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* `--task-file <path>` / `--task-file=<path>` を読む(純関数・読み込みは注入)。
|
|
87
|
+
* ⛔ 指定があるのに読めない・空なら invalid。呼び出し側は claude を起動せずに止める。
|
|
88
|
+
*/
|
|
89
|
+
export declare function readLauncherTask(argv: readonly string[], readFile: (p: string) => string): LauncherTask;
|
|
63
90
|
//# sourceMappingURL=headlessLaunch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headlessLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/headlessLaunch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;
|
|
1
|
+
{"version":3,"file":"headlessLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/headlessLaunch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAS5C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE/D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAwBzE;AAED,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,4CAA4C;IAC5C,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAclF;AAED,MAAM,MAAM,YAAY;AACtB,sCAAsC;AACpC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;AAClB,2BAA2B;GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAChC,gDAAgD;GAC9C;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,YAAY,CAwBvG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.isHeadlessArgv=isHeadlessArgv,exports.stripLauncherOwnedFlags=stripLauncherOwnedFlags,exports.buildClaudeArgs=buildClaudeArgs;const LAUNCHER_OWNED_VALUE_FLAGS=["--mcp-config"],LAUNCHER_OWNED_BOOLEAN_FLAGS=["--debug"],HEADLESS_FLAGS=["-p","--print"];function isHeadlessArgv(
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.TASK_FILE_FLAG=void 0,exports.isHeadlessArgv=isHeadlessArgv,exports.stripLauncherOwnedFlags=stripLauncherOwnedFlags,exports.buildClaudeArgs=buildClaudeArgs,exports.readLauncherTask=readLauncherTask,exports.TASK_FILE_FLAG="--task-file";const LAUNCHER_OWNED_VALUE_FLAGS=["--mcp-config",exports.TASK_FILE_FLAG],LAUNCHER_OWNED_BOOLEAN_FLAGS=["--debug"],HEADLESS_FLAGS=["-p","--print"];function isHeadlessArgv(e){return e.some(e=>"string"==typeof e&&HEADLESS_FLAGS.includes(e))}function stripLauncherOwnedFlags(e){const s=[];for(let r=0;r<e.length;r+=1){const t=e[r];if("string"!=typeof t)continue;if(LAUNCHER_OWNED_BOOLEAN_FLAGS.includes(t))continue;const n=LAUNCHER_OWNED_VALUE_FLAGS.find(e=>t===e||t.startsWith(`${e}=`));if(n){if(t===n){const s=e[r+1];"string"!=typeof s||s.startsWith("-")||(r+=1)}}else s.push(t)}return s}function buildClaudeArgs(e){const s=isHeadlessArgv(e.userArgs),r=stripLauncherOwnedFlags(e.userArgs);return{args:[...s?[]:[e.initialPrompt],"--dangerously-skip-permissions",...e.sysArgs,"--mcp-config",e.configPath,...r],headless:s}}function readLauncherTask(e,s){let r,t,n=!1;for(let s=0;s<e.length;s+=1){const t=e[s];if(t===exports.TASK_FILE_FLAG){n=!0;const t=e[s+1];r="string"!=typeof t||t.startsWith("-")?void 0:t}else"string"==typeof t&&t.startsWith(`${exports.TASK_FILE_FLAG}=`)&&(n=!0,r=t.slice(exports.TASK_FILE_FLAG.length+1)||void 0)}if(!n)return{kind:"none"};if(void 0===r)return{kind:"invalid",error:`${exports.TASK_FILE_FLAG} にファイルの path がありません`};try{t=s(r)}catch(e){return{kind:"invalid",error:`${exports.TASK_FILE_FLAG} ${r} を読めません: ${e instanceof Error?e.message:String(e)}`}}return 0===t.trim().length?{kind:"invalid",error:`${exports.TASK_FILE_FLAG} ${r} が空です`}:{kind:"task",text:t.trim()}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headlessLaunch.js","sourceRoot":"","sources":["../../src/lib/headlessLaunch.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG
|
|
1
|
+
{"version":3,"file":"headlessLaunch.js","sourceRoot":"","sources":["../../src/lib/headlessLaunch.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;AAsBH,wCAEC;AAMD,0DAwBC;AA2BD,0CAcC;AAcD,4CAwBC;AAnID;;;;;GAKG;AACU,QAAA,cAAc,GAAG,aAAa,CAAC;AAE5C,+CAA+C;AAC/C,MAAM,0BAA0B,GAAG,CAAC,cAAc,EAAE,sBAAc,CAAU,CAAC;AAC7E,MAAM,4BAA4B,GAAG,CAAC,SAAS,CAAU,CAAC;AAE1D,2CAA2C;AAC3C,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,SAAS,CAAU,CAAC;AAElD;;;;GAIG;AACH,SAAgB,cAAc,CAAC,IAAuB;IACpD,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAK,cAAoC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5G,CAAC;AAED;;;GAGG;AACH,SAAgB,uBAAuB,CAAC,IAAuB;IAC7D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAS;QAEtC,IAAK,4BAAkD,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QAEhF,MAAM,SAAS,GAAG,0BAA0B,CAAC,IAAI,CAC/C,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAC5C,CAAC;QACF,IAAI,SAAS,EAAE,CAAC;YACd,wDAAwD;YACxD,uEAAuE;YACvE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACzB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,CAAC,IAAI,CAAC,CAAC;YAChE,CAAC;YACD,SAAS;QACX,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAoBD;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,KAA2B;IACzD,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE1D,MAAM,IAAI,GAAG;QACX,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC1C,gCAAgC;QAChC,GAAG,KAAK,CAAC,OAAO;QAChB,cAAc;QACd,KAAK,CAAC,UAAU;QAChB,GAAG,SAAS;KACb,CAAC;IAEF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAUD;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,IAAuB,EAAE,QAA+B;IACvF,IAAI,IAAwB,CAAC;IAC7B,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,sBAAc,EAAE,CAAC;YAC3B,KAAK,GAAG,IAAI,CAAC;YACb,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9E,CAAC;aAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,sBAAc,GAAG,CAAC,EAAE,CAAC;YAC3E,KAAK,GAAG,IAAI,CAAC;YACb,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,sBAAc,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC;QAC3D,CAAC;IACH,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACpC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,sBAAc,qBAAqB,EAAE,CAAC;IAClG,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,sBAAc,IAAI,IAAI,YAAY,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACvH,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,sBAAc,IAAI,IAAI,OAAO,EAAE,CAAC;IAClG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AAC7C,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const{Server:Server}=require("@modelcontextprotocol/sdk/server/index.js"),{StdioServerTransport:StdioServerTransport}=require("@modelcontextprotocol/sdk/server/stdio.js"),{CallToolRequestSchema:CallToolRequestSchema,ListToolsRequestSchema:ListToolsRequestSchema}=require("@modelcontextprotocol/sdk/types.js"),{getPromptFromPortal:getPromptFromPortal,listAvailableMcps:listAvailableMcps,downloadMcpPackage:downloadMcpPackage,getGatewayConfig:getGatewayConfig,searchMcpServices:searchMcpServices,installMcpService:installMcpService,setServiceDeactivated:setServiceDeactivated,uninstallMcpService:uninstallMcpService,updateMcpEnv:updateMcpEnv,callMcpToolProxy:callMcpToolProxy}=require("./portal-api-client.cjs"),{requireFromPackage:requireFromPackage}=require("../requireFromPackage.cjs"),{logger:logger}=requireFromPackage(require,"../../utils/logger"),{t:t,setLanguage:setLanguage}=requireFromPackage(require,"../../i18n"),{describeTokenForDebug:describeTokenForDebug}=requireFromPackage(require,"../../lib/debugRedaction"),{collectUpdateNotices:collectUpdateNotices,buildUpdateNoticeText:buildUpdateNoticeText}=require("./mcp-version-advisor.cjs"),{installPackageFiles:installPackageFiles,formatInstallFailure:formatInstallFailure,runProductionNpmInstall:runProductionNpmInstall}=require("./install-package.cjs"),spawn=require("cross-spawn"),{StringDecoder:StringDecoder}=require("string_decoder"),path=require("path"),fs=require("fs"),os=require("os"),dotenvLocalPath=path.join(__dirname,"../../.env.local"),dotenvPath=path.join(__dirname,"../../.env");fs.existsSync(dotenvLocalPath)?(require("dotenv").config({path:dotenvLocalPath}),logger.debug("[MCP Server] Loaded .env.local")):fs.existsSync(dotenvPath)&&(require("dotenv").config({path:dotenvPath}),logger.debug("[MCP Server] Loaded .env"));const KEYWORD=process.env.MUSUHI_KEYWORD||"BL-要件定義",{TokenManager:TokenManager}=requireFromPackage(require,"../../auth/TokenManager");function resolveToken(){return TokenManager.loadTokenStringSync()}const PORTAL_URL_ENV=process.env.BLUELAMP_PORTAL_API_BASE_URL||process.env.PORTAL_URL||"",isDevEnv=PORTAL_URL_ENV.includes("localhost"),PORTAL_URL=PORTAL_URL_ENV||(isDevEnv?"http://localhost:8430/api/bluelamp":"https://api.motohi.jp/api/bluelamp"),LANGUAGE=process.env.MUSUHI_LANGUAGE||"ja",CATALOG_CACHE_FILE=path.join(os.homedir(),".bluelamp-vscode","gateway-catalog.json"),CATALOG_CACHE_VERSION=1,CHILD_READY_TIMEOUT_MS=3e4,CHILD_NOT_READY_TEXT="起動中です。数秒後に再試行してください";setLanguage(LANGUAGE);const LOG_DIR=path.join(os.homedir(),".bluelamp-vscode","logs"),LOG_FILE=path.join(LOG_DIR,"mcp.log");function log(e){const t=`[${(new Date).toISOString()}] ${e}\n`;fs.appendFileSync(LOG_FILE,t),logger.info(`[MCP Server] ${e}`)}fs.existsSync(LOG_DIR)||fs.mkdirSync(LOG_DIR,{recursive:!0}),log(`MCP Server started - Log file: ${LOG_FILE}`),log("Environment variables:"),log(` KEYWORD: ${KEYWORD}`),log(` TOKEN (起動時スナップショット): ${describeTokenForDebug(resolveToken())}`),log(` PORTAL_URL: ${PORTAL_URL}`),log(` LANGUAGE: ${LANGUAGE}`);const LEGACY_REGISTRY=path.join(os.homedir(),".bluelamp","registry.json");if(fs.existsSync(LEGACY_REGISTRY))try{fs.unlinkSync(LEGACY_REGISTRY),log(`[startup] Legacy registry.json removed: ${LEGACY_REGISTRY}`)}catch(e){log(`[startup] Failed to remove legacy registry.json: ${e.message}`)}class ChildMcpProcess{constructor(e,t,r,o){this.serviceId=e,this.command=t,this.args=(r||[]).map(e=>"string"==typeof e?e.replace(/^~\//,os.homedir()+"/"):e),this.env={...process.env,...o},this.process=null,this.tools=[],this.requestId=0,this.pending=new Map,this.buffer="",this.stdoutDecoder=new StringDecoder("utf8"),this.stderrDecoder=new StringDecoder("utf8"),this.stderrTail="",this._decodersEnded=!1}async start(){return new Promise((e,t)=>{let r=!1;const o=(e,t)=>{r||(r=!0,clearTimeout(s),e(t))},s=setTimeout(()=>{o(t,new Error(`Timeout: "${this.serviceId}" failed to start within 90s`)),this.stop()},9e4);try{this.process=spawn(this.command,this.args,{env:this.env,stdio:["pipe","pipe","pipe"]}),this.process.on("error",e=>{o(t,new Error(`Spawn error for "${this.serviceId}": ${e.message}`))}),this.process.stdin.on("error",e=>{log(`[ChildMcp:${this.serviceId}:stdin] ${e.message}`)}),this.process.on("close",()=>{this._flushDecoders("close")}),this.process.on("exit",(e,r)=>{log(`[ChildMcp] "${this.serviceId}" exited (code=${e}, signal=${r})`);const s=this.stderrTail.trim();o(t,new Error(`"${this.serviceId}" exited before ready (code=${e}, signal=${r})${s?` | stderr: ${s}`:""}`)),this.pending.forEach(e=>e.reject(new Error("Process exited"))),this.pending.clear()}),this.process.on("close",()=>{if(this._decodersEnded)return;this._decodersEnded=!0;const e=this.stdoutDecoder.end(),t=this.stderrDecoder.end();e&&(this.buffer+=e,log(`[ChildMcp] "${this.serviceId}" stdout に半端なバイトが ${Buffer.byteLength(e)} バイト残っていた(子が多バイト文字の途中で終了)⇒ buffer へ流した。⛔ これは「壊れた文字」であって「失われた文字」ではない`)),t&&log(`[ChildMcp] "${this.serviceId}" stderr に半端なバイトが ${Buffer.byteLength(t)} バイト残っていた ⇒ ${JSON.stringify(t)}`)}),this.process.stderr.on("data",e=>{const t=this.stderrDecoder.write(e);this.stderrTail=sliceTailPreservingSurrogates(this.stderrTail+t,500),log(`[ChildMcp:${this.serviceId}:stderr] ${t.trim()}`)}),this.process.stdout.on("data",e=>{this.buffer+=this.stdoutDecoder.write(e),this._processBuffer()}),this._sendRequest("initialize",{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"bluelamp-gateway",version:"1.0.0"}}).then(e=>(log(`[ChildMcp] "${this.serviceId}" initialized: ${JSON.stringify(e.serverInfo||{})}`),this._sendNotification("notifications/initialized",{}),this._sendRequest("tools/list",{}))).then(t=>{const r=t.tools||[];this.tools=r.map(e=>({...e,name:`${this.serviceId}__${e.name}`,_originalName:e.name})),log(`[ChildMcp] "${this.serviceId}" ready: ${this.tools.length} tools`),o(e,this.tools)}).catch(e=>{o(t,e)})}catch(e){o(t,e)}})}async callTool(e,t){return await this._sendRequest("tools/call",{name:e,arguments:t})}_flushDecoders(e){for(const[t,r]of[["stdout",this.stdoutDecoder],["stderr",this.stderrDecoder]]){let o="";try{o=r.end()}catch(e){log(`[ChildMcp:${this.serviceId}:${t}] decoder.end() が落ちた: ${e.message}`);continue}o&&log(`[ChildMcp:${this.serviceId}:${t}] 🚨 未完の多バイト列を ${e} 時に検出: ${JSON.stringify(o)}(${o.length} 文字・⛔ 黙って捨てていない)`)}}stop(){this.process&&(this.process.kill(),this.process=null),this.pending.forEach(e=>e.reject(new Error("Process stopped"))),this.pending.clear()}_sendRequest(e,t){return new Promise((r,o)=>{const s=++this.requestId,i=JSON.stringify({jsonrpc:"2.0",id:s,method:e,params:t})+"\n";this.pending.set(s,{resolve:r,reject:o});try{this.process.stdin.write(i)}catch(e){this.pending.delete(s),o(new Error(`Write error: ${e.message}`))}})}_sendNotification(e,t){const r=JSON.stringify({jsonrpc:"2.0",method:e,params:t})+"\n";try{this.process.stdin.write(r)}catch(e){log(`[ChildMcp] Notification write error: ${e.message}`)}}_processBuffer(){const e=this.buffer.split("\n");this.buffer=e.pop();for(const t of e){const e=t.trim();if(e)try{const t=JSON.parse(e);if(t.id&&this.pending.has(t.id)){const e=this.pending.get(t.id);this.pending.delete(t.id),t.error?e.reject(new Error(`JSON-RPC error: ${JSON.stringify(t.error)}`)):e.resolve(t.result)}}catch(t){log(`[ChildMcp:${this.serviceId}] Parse error: ${t.message} | line: ${e.substring(0,200)}`)}}}}class ChildMcpManager{constructor(){this.children=new Map,this.starting=new Map}async activateExternal(e,t){const r=new ChildMcpProcess(e,t.command,t.args||[],t.env||{}),o=await r.start();return this.children.set(e,{type:"external",process:r,tools:o,service:t}),o}beginActivateExternal(e,t){const r=this.children.get(e);if(r)return Promise.resolve({ok:!0,tools:r.tools||[]});const o=this.starting.get(e);if(o)return o;const s=this.activateExternal(e,t).then(e=>({ok:!0,tools:e}),e=>({ok:!1,error:e})).then(t=>(this.starting.delete(e),t));return this.starting.set(e,s),s}isStarting(e){return this.starting.has(e)}async waitForReady(e,t){if(this.children.has(e))return{ready:!0};const r=this.starting.get(e);if(!r)return{ready:!1,reason:"unknown"};let o=null;const s=new Promise(e=>{o=setTimeout(()=>e({timedOut:!0}),t),"function"==typeof o.unref&&o.unref()});try{const e=await Promise.race([r,s]);return e.timedOut?{ready:!1,reason:"timeout"}:e.ok?{ready:!0}:{ready:!1,reason:"failed",error:e.error}}finally{o&&clearTimeout(o)}}deactivate(e){const t=this.children.get(e);t&&("external"===t.type&&t.process&&t.process.stop(),this.children.delete(e))}isActive(e){return this.children.has(e)}getActiveTools(){const e=[];for(const t of this.children.values())e.push(...t.tools||[]);return e}async callTool(e,t){for(const[r,o]of this.children)if("external"===o.type){const r=o.tools.find(t=>t.name===e);if(r)return{found:!0,result:await o.process.callTool(r._originalName,t)}}return{found:!1}}getActiveServiceIds(){return[...this.children.keys()]}}class BlueLampServer{constructor(){this.childManager=new ChildMcpManager,this.updateNoticeText=null,this._catalogTools=new Map,this._cloudProxyTools=new Map,this._catalogServices=new Map,this._needChildForSchema=new Set,this._catalogFromCache=!1,this._configRefresh=null,this.server=new Server({name:"bluelamp",version:"2.0.0"},{capabilities:{tools:{listChanged:!0}}}),this.setupHandlers()}checkAuth(){return resolveToken()?{ok:!0}:{ok:!1,error:"BlueLamp にログインしてください。ターミナルで `bluelamp1` を一度実行すると ~/.musuhi/portal-token.enc が作成されます。"}}buildToolList(){return buildGatewayToolList([{name:"load_knowledge",description:t("mcp.toolDescriptionLoadKnowledge"),annotations:{readOnlyHint:!0,idempotentHint:!0},inputSchema:{type:"object",properties:{keyword:{type:"string",description:t("mcp.keywordDescription")}},required:["keyword"]}},{name:"search_mcp",description:'キーワードでインストール済みMCPサービスを検索します。使いたい機能を自然言語で説明してください。(例: "ブラウザ操作", "メール送信", "画像生成")',inputSchema:{type:"object",properties:{query:{type:"string",description:"検索キーワード(自然言語OK)"}},required:["query"]}},{name:"activate_service",description:"deactivate_service で一覧から外した MCP を、道具一覧に戻します(deactivate を戻すだけ)。権限のある MCP は最初から一覧に出ているので、通常このツールを撃つ必要はありません。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"サービスID"},always_on:{type:"boolean",description:"後方互換のため受け付けますが、一覧の可否には使いません(無視されます)"}},required:["service_id"]}},{name:"deactivate_service",description:"指定した MCP を道具一覧から外します(deactivated=true)。次の接続でも一覧に出なくなります。権限は変わらないので activate_service でいつでも戻せます。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"サービスID"}},required:["service_id"]}},{name:"restart_service",description:"MCPサービスのプロセスを再起動します。コード更新や設定変更の反映に使用します。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"サービスID"}},required:["service_id"]}},{name:"unregister_service",description:"MCPサービスをレジストリから完全に削除します。プロセスも停止します。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"削除するサービスID"}},required:["service_id"]}},{name:"register_service",description:"新しいMCPサービスをゲートウェイに登録します。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:'サービスID(英数字、例: "microcms")'},name:{type:"string",description:"サービス表示名"},description:{type:"string",description:"サービスの説明"},tags:{type:"array",items:{type:"string"},description:"検索用タグ"},command:{type:"string",description:'実行コマンド("npx" or "node")'},args:{type:"array",items:{type:"string"},description:"コマンド引数"},env:{type:"object",description:"環境変数"}},required:["service_id","name","description","tags"]}},{name:"browse_mcp_store",description:"MCPストア(Portal)からインストール可能なMCP一覧を取得します。",inputSchema:{type:"object",properties:{},required:[]}},{name:"install_from_store",description:"MCP ストアから MCP を取得してこの機体に展開し、env と導入版をサーバに記録します。browse_mcp_store で見つけた mcp_id を指定してください。道具一覧に出るかどうかは権限で決まるので、権限の無い MCP をこれで一覧に出すことはできません(自分で作った MCP・共有された MCP は権限に含まれるため、何もしなくても一覧に出ます)。",inputSchema:{type:"object",properties:{mcp_id:{type:"string",description:"インストールするMCPのID"}},required:["mcp_id"]}},{name:"save_mcp_env",description:"MCPの環境変数(APIキー等)をサーバーに安全に保存します。ローカルには保存されません。次回起動時にサーバーから自動取得されます。解約時はアクセスできなくなります。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"MCP サービスID(例: heygen, gemini-image)"},env:{type:"object",description:'環境変数のキーと値(例: {"HEYGEN_API_KEY": "sk_xxx"})',additionalProperties:{type:"string"}}},required:["service_id","env"]}}],this._catalogTools,this.childManager.getActiveTools())}applyCatalog(e,t={}){const{catalog:r,cloudProxy:o,warnings:s,needChildForSchema:i}=buildCatalogRegistry(e);this._catalogTools=r,this._cloudProxyTools=o,this._catalogServices=new Map,this._needChildForSchema=new Set(i),this._catalogFromCache=!0===t.fromCache;for(const t of Array.isArray(e)?e:[])t&&t.mcpId&&this._catalogServices.set(t.mcpId,t);let n=0;for(const e of s)"no-input-schema"===e.reason?(n+=e.count,log(`[catalog] "${e.mcpId}" (${e.hostingType||"unknown"}): ${e.count}/${e.total} tools have no inputSchema — 一覧には出す(空 schema)。子を起こして実 schema で上書きする`)):log(`[catalog] "${e.mcpId}" (${e.hostingType||"unknown"}) skipped: ${e.reason}`);return n>0&&log(`[catalog] inputSchema 欠損の合計: ${n} 本 / ${this._needChildForSchema.size} service(一覧には出す。子を起こして実 schema で上書きする)`),log(`[catalog] ${r.size} tools registered from ${this._catalogServices.size} services (cloud_proxy: ${o.size})`),r}registerServiceInCatalog(e){const{catalog:t,cloudProxy:r,warnings:o}=buildCatalogRegistry([e]);for(const[e,r]of t)this._catalogTools.set(e,r);for(const[e,t]of r)this._cloudProxyTools.set(e,t);for(const e of o)log(`[catalog] "${e.mcpId}" (${e.hostingType||"unknown"}) skipped: ${e.reason}`);return e&&e.mcpId&&this._catalogServices.set(e.mcpId,e),t.size}unregisterServiceFromCatalog(e){const t=`${e}__`;let r=0;for(const e of[...this._catalogTools.keys()])e.startsWith(t)&&(this._catalogTools.delete(e),this._cloudProxyTools.delete(e),r++);return r}buildChildService(e,t){const r=path.join(os.homedir(),".claude","mcp-servers",e),o=t.entryPoint||"dist/index.js";return{name:t.name,description:t.description,type:"external",command:t.command||"node",args:t.args&&t.args.length?t.args:[path.join(r,o)],env:t.env||{}}}setupHandlers(){this.server.setRequestHandler(ListToolsRequestSchema,async()=>{const e=this.buildToolList(),t=this.childManager.getActiveServiceIds();return log(`[ListTools] ${e.length} tools (active: ${t.join(", ")||"none"})`),{tools:e}}),this.server.setRequestHandler(CallToolRequestSchema,async e=>{const{name:r,arguments:o}=e.params;log(`[CallTool] ${r}`);const s=resolveToken();if("load_knowledge"===r){const{keyword:e}=o;log(`[load_knowledge] keyword: ${e}`);try{const r=await getPromptFromPortal(e,s,LANGUAGE);if(!r||!r.success){const o=r?.error||t("mcp.knowledgeNotFound",{keyword:e});return{content:[{type:"text",text:`❌ ${t("mcp.portalApiFailed",{error:o})}`}],isError:!0}}const o="ja"!==LANGUAGE?"\n\n[Note] The user's preferred language is English.":"",i=this.updateNoticeText?`\n\n${this.updateNoticeText}`:"";return{content:[{type:"text",text:`✅ ${t("mcp.knowledgeLoadSuccess")}\n\n${r.knowledge}${o}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${t("mcp.knowledgeApplied")}${i}`}]}}catch(e){return log(`[load_knowledge] Exception: ${e.message}`),{content:[{type:"text",text:`❌ ${t("mcp.knowledgeLoadError",{error:e.message})}`}],isError:!0}}}if("search_mcp"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};if(!s)return{content:[{type:"text",text:"❌ 未認証です。MCPカタログの検索にはログインが必要です。"}],isError:!0};let t=[];try{const e=await searchMcpServices(o.query,s);if(!e.success)return{content:[{type:"text",text:`❌ MCPカタログ検索に失敗: ${e.error||"Unknown"}`}],isError:!0};t=(e.data||[]).map(e=>({id:e.mcp_id,service:{name:e.name,description:e.description,tags:[],alwaysOn:e.alwaysOn||!1},isInstalled:e.isInstalled||!1}))}catch(e){return log(`[search_mcp] Server search failed: ${e.message}`),{content:[{type:"text",text:`❌ MCPカタログ検索に失敗: ${e.message}`}],isError:!0}}if(0===t.length)return{content:[{type:"text",text:`🔍 「${o.query}」に一致するサービスはありません。\n\n別のキーワードで試すか、browse_mcp_storeでカタログを確認してください。`}]};let r=`🔍 「${o.query}」の検索結果(${t.length}件)\n\n`;for(const e of t.slice(0,10)){const t=e.id,o=e.service,s=this.childManager.isActive(t),i=!0===o.deactivated,n=e.isInstalled?"":" [未インストール]",a=i?" [一覧から外し中]":"";r+=`${i?"🚫":s?"🟢":"⚪"} **${o.name}**${a}${n} (id: \`${t}\`)\n`,r+=` ${o.description}\n\n`}return r+="一覧に戻す: activate_service | ストアから導入: install_from_store",{content:[{type:"text",text:r}]}}if("activate_service"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};const{service_id:t}=o,r=await getGatewayConfig(s);if(!r.success)return{content:[{type:"text",text:`❌ サーバー設定の取得に失敗: ${r.error||"Unknown"}`}],isError:!0};const i=(r.data?.services||[]).find(e=>e.mcpId===t);if(!i)return{content:[{type:"text",text:`❌ サービス "${t}" はあなたの権限の中にありません(権限のある MCP は何もしなくても一覧に出ます)。`}],isError:!0};const n=await setServiceDeactivated(t,!1,s);if(!n.success)return{content:[{type:"text",text:`❌ "${t}" を一覧に戻せませんでした: ${n.error||"Unknown"}`}],isError:!0};log(`[activate] "${t}" deactivated=false (一覧に戻した)`);const a=this.registerServiceInCatalog({...i,deactivated:!1});if(0===a)return{content:[{type:"text",text:`❌ "${t}" には目録にツール定義がありません(tools が空)。`}],isError:!0};if("cloud_proxy"===i.hostingType){log(`[activate] "${t}" (cloud_proxy) → ${a} proxy tools registered`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[activate] notification error: ${e.message}`)}return{content:[{type:"text",text:`✅ "${i.name}" を一覧に戻しました(cloud_proxy ツール${a}個)`}]}}if(this.childManager.isActive(t)){try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[activate] notification error: ${e.message}`)}return{content:[{type:"text",text:`✅ "${t}" を一覧に戻しました(子プロセスは既に起動済み)。`}]}}log(`[activate] "${t}" (${i.hostingType}) → ${a} catalog tools restored. Sending list_changed.`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[activate] notification error: ${e.message}`)}return{content:[{type:"text",text:`✅ "${i.name}" を一覧に戻しました(ツール${a}個)。最初に呼ばれた時に自動で起動します。`}]}}if("deactivate_service"===r){const{service_id:e}=o;if(!s)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};const t=await setServiceDeactivated(e,!0,s);if(!t.success)return{content:[{type:"text",text:`❌ "${e}" を一覧から外せませんでした: ${t.error||"Unknown"}`}],isError:!0};log(`[deactivate] "${e}" deactivated=true (一覧から外した)`);const r=this.unregisterServiceFromCatalog(e);r>0&&log(`[deactivate] "${e}" → ${r} catalog tools removed`),this._catalogServices.delete(e);let i=!1;if(this.childManager.isActive(e)&&(this.childManager.deactivate(e),log(`[deactivate] "${e}" child process stopped`),i=!0),r>0||i)try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[deactivate] notification error: ${e.message}`)}let n=`✅ "${e}" を道具一覧から外しました(deactivated=true)。`;return r>0&&(n+=` ツール${r}個を一覧から削除しました。`),i&&(n+=" 子プロセスを停止しました。"),n+=" 次の接続でも一覧に出ません。権限は変わっていないので activate_service で戻せます。",{content:[{type:"text",text:n}]}}if("restart_service"===r){const{service_id:e}=o,t=this.childManager.children.get(e),r=t?.service;if(!r)return isCloudProxyService(this._cloudProxyTools,e)?{content:[{type:"text",text:`ℹ️ "${e}" は cloud_proxy 方式のため、ローカルの再起動対象ではありません(子プロセスを持ちません)。\nサーバ側のツール更新は deploy で全利用者に反映されるので、再起動は不要です。\nツール一覧を取り直したいだけなら deactivate_service → activate_service を実行してください。`}]}:{content:[{type:"text",text:`❌ "${e}" の子プロセスは現在動いていません。道具を呼べば自動で起動します(restart は起動済みの子にだけ効きます)。`}],isError:!0};let i,n=r;if(s)try{const t=await getGatewayConfig(s);if(t.success){const o=(t.data?.services||[]).find(t=>t.mcpId===e);o&&o.env&&(n={...r,env:o.env},log(`[restart] "${e}" env refreshed from server (keys=${Object.keys(o.env).join(",")||"none"})`))}}catch(e){log(`[restart] env refresh failed (using cached): ${e.message}`)}this.childManager.deactivate(e),log(`[restart] "${e}" stopped`);try{i=await this.childManager.activateExternal(e,n)}catch(t){return log(`[restart] Failed "${e}": ${t.message}`),{content:[{type:"text",text:`❌ "${e}" の再起動に失敗しました: ${t.message}`}],isError:!0}}log(`[restart] "${e}" → ${i.length} tools. Sending list_changed.`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[restart] notification error: ${e.message}`)}const a=i.map(e=>e.name).join(", ");return{content:[{type:"text",text:`🔄 "${n.name}" を再起動しました(ツール${i.length}個)\n\nツール: ${a||"(なし)"}`}]}}if("unregister_service"===r){const{service_id:e}=o;if(!s)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};this.childManager.isActive(e)&&(this.childManager.deactivate(e),log(`[unregister] "${e}" stopped`));const t=await uninstallMcpService(e,s);if(!t.success)return{content:[{type:"text",text:`❌ "${e}" のサーバー削除に失敗: ${t.error||"Unknown"}`}],isError:!0};log(`[unregister] "${e}" uninstalled from server`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[unregister] notification error: ${e.message}`)}return{content:[{type:"text",text:`🗑️ "${e}" を削除しました。プロセスも停止済みです。`}]}}if("register_service"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};const{service_id:t,env:r}=o;if(!s)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};const i=r||{},n=await installMcpService(t,!1,s,i);return n.success?(log(`[register] "${t}" installed on server`),{content:[{type:"text",text:`✅ サービス "${t}" をサーバーに登録しました。\n\n次のステップ:\n- save_mcp_env で APIキー等を設定\n- 次の接続で道具一覧に出ます(activate は不要です)`}]}):{content:[{type:"text",text:`❌ "${t}" の登録に失敗しました: ${n.error||"Unknown"}\n\n※ MCP はサーバーカタログに登録されている必要があります。browse_mcp_store で確認してください。`}],isError:!0}}if("save_mcp_env"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};const{service_id:t,env:r}=o;if(!t||!r||0===Object.keys(r).length)return{content:[{type:"text",text:"❌ service_id と env(キー/値のペア)が必要です"}],isError:!0};log(`[save_mcp_env] ${t}: ${Object.keys(r).join(", ")}`);try{const e=await updateMcpEnv(t,r,s);if(!e.success)return{content:[{type:"text",text:`❌ サーバー保存失敗: ${e.error}`}],isError:!0};const o=Object.keys(r).join(", ");return e.warning?{content:[{type:"text",text:`🔐 環境変数をサーバーに保存しました\n\nサービス: ${t}\n保存キー: ${o}\n\n${e.warning}`}]}:{content:[{type:"text",text:`🔐 環境変数をサーバーに保存しました\n\nサービス: ${t}\n保存キー: ${o}\n\n※ ローカルには保存されません。次回起動時にサーバーから自動取得されます。`}]}}catch(e){return log(`[save_mcp_env] error: ${e.message}`),{content:[{type:"text",text:`❌ ${e.message}`}],isError:!0}}}if("browse_mcp_store"===r){log("[browse_mcp_store]");try{const e=await listAvailableMcps({},s,LANGUAGE);if(!e||!e.success)return{content:[{type:"text",text:`❌ MCPカタログの取得に失敗: ${e?.error||"Unknown"}`}],isError:!0};const t=e.data||[];if(0===t.length)return{content:[{type:"text",text:"📦 インストール可能なMCPはありません。"}]};let r=`📦 MCPストア一覧 (${t.length}件)\n\n`;return t.forEach(e=>{const t=e.isOfficial?" [公式]":"";r+=`**${e.name}**${t}\n`,r+=` ID: ${e.mcpId}\n`,r+=` ${e.description}\n`,r+=` ツール数: ${e.toolCount}\n\n`}),r+='\nインストールするには: install_from_store(mcp_id: "MCP_ID")',{content:[{type:"text",text:r}]}}catch(e){return log(`[browse_mcp_store] error: ${e.message}`),{content:[{type:"text",text:`❌ ${e.message}`}],isError:!0}}}if("install_from_store"===r){const{mcp_id:e}=o;log(`[install_from_store] mcp_id=${e}`);try{const t=await downloadMcpPackage(e,s);if(!t||!t.success)return{content:[{type:"text",text:`❌ MCPダウンロードに失敗: ${t?.error||"Unknown"}`}],isError:!0};const r=t.data,o=r.packageSource||{},i=r.files||[];let n=o.hostingType||o.hosting&&o.hosting.type||null;if(s){let t=!1;try{const r=await getGatewayConfig(s);if(r.success){const o=(r.data?.services||[]).find(t=>t.mcpId===e);o&&(t=!0===o.alwaysOn,n||(n=o.hostingType))}}catch(e){}const r=await installMcpService(e,t,s);if(!r.success)return{content:[{type:"text",text:`❌ サーバー登録失敗: ${r.error||"Unknown"}`}],isError:!0};log(`[install_from_store] Server sync OK: ${e} (alwaysOn=${t})`)}if("cloud_proxy"===n){log(`[install_from_store] "${e}" is cloud_proxy, skipping local extract`);let t=`✅ MCP「${o.name||e}」をインストールしました(cloud_proxy)\n\n`;return t+="☁ クラウド実行のためローカル展開は不要です\n",o.installNotes&&(t+=`\n📝 ${o.installNotes}\n`),t+="\n権限に含まれていれば、次の接続で道具一覧に出ます(activate_service を撃つ必要はありません)。",{content:[{type:"text",text:t}]}}if(0===i.length)return{content:[{type:"text",text:"❌ MCPパッケージにファイルが含まれていません。"}],isError:!0};const a=path.join(os.homedir(),".claude","mcp-servers",e),c=installPackageFiles({installDir:a,files:i,needsNpmInstall:!!o.needsNpmInstall,runNpmInstall:e=>runProductionNpmInstall(e)});if(!c.ok)return log(`[install_from_store] ${e} failed at ${c.stage}: ${c.error.message}`),{content:[{type:"text",text:formatInstallFailure(o.name||e,c)}],isError:!0};log(`[install_from_store] ${i.length} files written to ${a}`);let l=`✅ MCP「${o.name||e}」をインストールしました\n\n`;l+=`📁 インストール先: ${a}\n`,l+=`📄 ファイル数: ${i.length}\n`,o.installNotes&&(l+=`\n📝 ${o.installNotes}\n`);const d=(o.requiredEnvVars||[]).filter(e=>!process.env[e]);return d.length>0&&(l+="\n⚠️ 以下の環境変数を save_mcp_env で設定してください:\n",d.forEach(e=>{l+=` - ${e}\n`})),l+="\n権限に含まれていれば、次の接続で道具一覧に出ます(activate_service を撃つ必要はありません)。",{content:[{type:"text",text:l}]}}catch(e){return log(`[install_from_store] error: ${e.message}`),{content:[{type:"text",text:`❌ ${e.message}`}],isError:!0}}}if(this._cloudProxyTools&&this._cloudProxyTools.has(r)){const e=this._cloudProxyTools.get(r);log(`[callTool] Cloud proxy: ${r} → ${e.mcpId}/${e.toolName}`);try{const t=await callMcpToolProxy(e.mcpId,e.toolName,o,s);return t.success&&t.data?t.data.content?t.data:{content:[{type:"text",text:JSON.stringify(t.data,null,2)}]}:{content:[{type:"text",text:`❌ プロキシエラー: ${t.error||"Unknown"}`}],isError:!0}}catch(e){return log(`[callTool] Cloud proxy error "${r}": ${e.message}`),{content:[{type:"text",text:`❌ クラウドプロキシエラー: ${e.message}`}],isError:!0}}}try{const{found:e,result:t}=await this.childManager.callTool(r,o);if(e)return t&&t.content?t:{content:[{type:"text",text:String(t)}]}}catch(e){return log(`[callTool] Error proxying "${r}": ${e.message}`),{content:[{type:"text",text:`❌ ツール実行エラー: ${e.message}`}],isError:!0}}const i=this._catalogTools.get(r);if(i&&"cloud_proxy"!==i.hostingType){const e=i.mcpId;this._catalogFromCache&&this._configRefresh&&(log(`[callTool] "${e}" は目録キャッシュ由来。起動仕様の到着を待つ。`),await this._configRefresh),this.startChildOnDemand(e);const t=await this.childManager.waitForReady(e,3e4);if(!t.ready){if("failed"===t.reason){const r=t.error?.message||String(t.error||"Unknown");return log(`[callTool] "${e}" failed to start: ${r}`),{content:[{type:"text",text:`❌ "${e}" の起動に失敗しました: ${r}`}],isError:!0}}return"timeout"===t.reason?(log(`[callTool] "${e}" not ready within 30000ms for "${r}".`),{content:[{type:"text",text:CHILD_NOT_READY_TEXT}],isError:!0}):(log(`[callTool] "${e}" could not be started for "${r}" (reason=${t.reason}).`),{content:[{type:"text",text:`❌ "${e}" を起動できませんでした(設定が見つかりません)。`}],isError:!0})}try{const{found:t,result:s}=await this.childManager.callTool(r,o);if(t)return s&&s.content?s:{content:[{type:"text",text:String(s)}]};log(`[callTool] "${r}" is in catalog but child "${e}" does not have it.`)}catch(e){return log(`[callTool] Error proxying "${r}" after ready: ${e.message}`),{content:[{type:"text",text:`❌ ツール実行エラー: ${e.message}`}],isError:!0}}}return{content:[{type:"text",text:`❌ Unknown tool: ${r}`}],isError:!0}})}startChildrenForMissingSchema(){let e=0;for(const t of this._needChildForSchema){const r=this._catalogServices.get(t);if(r)if("cloud_proxy"!==r.hostingType){if(!this.childManager.isActive(t)&&!this.childManager.isStarting(t)){log(`[schemaChild] "${t}" (${r.hostingType}) を起こして実 schema を取りに行く`);try{this.startChildOnDemand(t),e++}catch(e){log(`[schemaChild] "${t}" の起動に失敗: ${e.message}(⛔ 席は落とさない)`)}}}else log(`[schemaChild] "${t}" は cloud_proxy のため子を起こせない。schema 欠損の道具は一覧に出ない。`)}return log(`[schemaChild] ${e} service(=目録に schema が無い道具を持つ service の数)`),e}async loadCatalog(e){if(!e)return log("[catalog] No token, starting with no catalog tools."),[];let t=[];try{const r=await getGatewayConfig(e);if(!r.success)return log(`[catalog] Server config failed: ${r.error||"Unknown"}. Starting with no services.`),[];t=r.data?.services||[],log(`[catalog] Server config: ${t.length} services`),this.applyCatalog(t),writeCatalogCache(CATALOG_CACHE_FILE,t,catalogCacheFingerprint(e),log)}catch(e){return log(`[catalog] Server config error: ${e.message}. Starting with no services.`),[]}try{const e=collectUpdateNotices(t);this.updateNoticeText=buildUpdateNoticeText(e);for(const t of e)"not-applicable"!==t.status&&log(`[versionCheck] ${t.mcpId}: ${t.status} (${t.reason}) catalog=${t.catalogVersion} local=${t.localVersion}`)}catch(e){log(`[versionCheck] skipped: ${e.message}`)}return t}startChildOnDemand(e){const t=this._catalogServices.get(e);if(!t||!0===t.deactivated)return null;if("cloud_proxy"===t.hostingType)return null;if(this.childManager.isActive(e))return null;const r=this.childManager.isStarting(e),o=this.childManager.beginActivateExternal(e,this.buildChildService(e,t));return r||log(`[startChild] "${e}" (${t.hostingType}) starting on demand...`),o.then(t=>this.reconcileChildWithCatalog(e,t)).catch(t=>log(`[startChild] "${e}" post-start error: ${t.message}`)),o}async reconcileChildWithCatalog(e,t){const r=decideCatalogReconciliation(this._catalogTools,e,t);if(!r.ready){const o=t&&t.error?`: ${t.error.message||t.error}`:"";return log(`[versionDrift] "${e}" not ready (${r.reason})${o} → 剪定も通知もしない`),r}if(!r.notify)return log(`[versionDrift] "${e}" matches catalog (${t.tools.length} tools) → no list_changed`),r;for(const e of r.prune)this._catalogTools.delete(e),this._cloudProxyTools.delete(e);const o=r.diff;log(`[versionDrift] "${e}" differs from catalog: +${o.added.length} -${o.removed.length} ~${o.changed.length} → list_changed`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[versionDrift] notification error: ${e.message}`)}return r}toolListSignature(){return stableJson(this.buildToolList().map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema})))}async refreshCatalogInBackground(e){const t=this.toolListSignature();try{await this.loadCatalog(e)}catch(e){return log(`[catalogRefresh] failed, keeping cached catalog: ${e.message}`),{notified:!1,reason:"failed"}}if(t===this.toolListSignature())return log("[catalogRefresh] server catalog matches cache → no list_changed"),{notified:!1,reason:"same"};log("[catalogRefresh] server catalog differs from cache → list_changed");try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[catalogRefresh] notification error: ${e.message}`)}return{notified:!0,reason:"diff"}}async run(){const e=resolveToken(),t=catalogCacheFingerprint(e),r=readCatalogCache(CATALOG_CACHE_FILE,t,log);r?(this.applyCatalog(r,{fromCache:!0}),log(`[catalogCache] ${this._catalogTools.size} tools from cache (HTTP は待たない)`)):(log("[catalogCache] no usable cache, waiting for server config"),await this.loadCatalog(e));const o=new StdioServerTransport;await this.server.connect(o),log(`[Gateway] BlueLamp Gateway v2.0.0 started. (${this._catalogTools.size} catalog tools available at connect)`),r?(this._configRefresh=this.refreshCatalogInBackground(e).catch(e=>(log(`[catalogRefresh] unexpected: ${e.message}`),{notified:!1,reason:"failed"})),this._configRefresh.then(()=>this.startChildrenForMissingSchema()).catch(e=>log(`[schemaChild] deferred start failed: ${e.message}`))):this.startChildrenForMissingSchema();const s=resolveToken();if(KEYWORD&&s){log(`[Gateway] Auto-loading knowledge for keyword: ${KEYWORD}`);const e=await getPromptFromPortal(KEYWORD,s,LANGUAGE);e&&e.success?log(`[Gateway] Auto-load successful: ${e.title}`):log(`[Gateway] Auto-load failed for keyword: ${KEYWORD}`)}}}function buildCatalogRegistry(e){const t=new Map,r=new Map,o=[],s=[];for(const i of Array.isArray(e)?e:[]){if(!i||"string"!=typeof i.mcpId||!i.mcpId)continue;const e=i.hostingType||null;if(!0===i.deactivated)continue;const n=Array.isArray(i.tools)?i.tools:[];if(0===n.length){o.push({mcpId:i.mcpId,hostingType:e,reason:"no-tools"});continue}const a=[];for(const s of n){if(!s||"string"!=typeof s.name||!s.name){o.push({mcpId:i.mcpId,hostingType:e,reason:"unnamed-tool"});continue}s.inputSchema&&"object"==typeof s.inputSchema||a.push(s.name);const n=`${i.mcpId}__${s.name}`,c={name:n,mcpId:i.mcpId,toolName:s.name,hostingType:e,description:s.description,inputSchema:s.inputSchema};t.set(n,c),"cloud_proxy"===e&&r.set(n,{mcpId:i.mcpId,toolName:s.name,description:s.description,inputSchema:s.inputSchema})}a.length>0&&(o.push({mcpId:i.mcpId,hostingType:e,reason:"no-input-schema",count:a.length,total:n.length,tools:a}),s.push(i.mcpId))}return{catalog:t,cloudProxy:r,warnings:o,needChildForSchema:s}}function catalogEntryToTool(e){return{name:e.name,description:e.description||`[${e.mcpId}] ${e.toolName}`,inputSchema:e.inputSchema||{type:"object",properties:{}}}}function buildGatewayToolList(e,t,r){const o=new Map;for(const t of Array.isArray(e)?e:[])t&&t.name&&o.set(t.name,t);if(t&&"function"==typeof t.values)for(const e of t.values())o.has(e.name)||o.set(e.name,catalogEntryToTool(e));for(const e of Array.isArray(r)?r:[])e&&e.name&&o.set(e.name,e);return[...o.values()]}function sanitizeServicesForCache(e){return(Array.isArray(e)?e:[]).filter(e=>e&&"string"==typeof e.mcpId&&e.mcpId).map(e=>({mcpId:e.mcpId,name:e.name,description:e.description,hostingType:e.hostingType||null,deactivated:!0===e.deactivated,tools:(Array.isArray(e.tools)?e.tools:[]).filter(e=>e&&"string"==typeof e.name&&e.name).map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}))}))}function catalogCacheFingerprint(e){return e?require("crypto").createHash("sha256").update(String(e)).digest("hex").slice(0,16):null}function sliceTailPreservingSurrogates(e,t){const r=String(null==e?"":e);if(r.length<=t)return r;const o=r.slice(-t),s=o.charCodeAt(0);return s>=56320&&s<=57343?o.slice(1):o}function stableJson(e){return void 0===e?"null":null===e||"object"!=typeof e?JSON.stringify(e):Array.isArray(e)?`[${e.map(stableJson).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${stableJson(e[t])}`).join(",")}}`}function diffChildToolsAgainstCatalog(e,t,r){const o=`${t}__`,s=new Set;if(e&&"function"==typeof e.keys)for(const t of e.keys())"string"==typeof t&&t.startsWith(o)&&s.add(t);const i=[],n=[],a=new Set;for(const t of Array.isArray(r)?r:[]){if(!t||!t.name)continue;a.add(t.name);const r=e&&"function"==typeof e.get?e.get(t.name):void 0;r?stableJson(r.description)===stableJson(t.description)&&stableJson(r.inputSchema)===stableJson(t.inputSchema)||n.push(t.name):i.push(t.name)}const c=[...s].filter(e=>!a.has(e));return{added:i,removed:c,changed:n,hasDiff:i.length+c.length+n.length>0}}function decideCatalogReconciliation(e,t,r){if(!r||!0!==r.ok||!Array.isArray(r.tools))return{ready:!1,prune:[],notify:!1,reason:r&&r.reason||"not-ready",diff:null};const o=diffChildToolsAgainstCatalog(e,t,r.tools);return{ready:!0,prune:o.removed,notify:o.hasDiff,reason:"ready",diff:o}}function readCatalogCache(e,t,r){const o=r||(()=>{});if(!t)return null;let s,i;try{s=fs.readFileSync(e,"utf8")}catch(e){return"ENOENT"!==e.code&&o(`[catalogCache] read failed: ${e.message}`),null}try{i=JSON.parse(s)}catch(e){return o(`[catalogCache] broken JSON, ignoring: ${e.message}`),null}return i&&1===i.version?i.tokenFingerprint!==t?(o("[catalogCache] fingerprint mismatch (口座が違う), ignoring"),null):Array.isArray(i.services)?i.services:(o("[catalogCache] services が配列でない, ignoring"),null):(o(`[catalogCache] version mismatch (${i&&i.version}), ignoring`),null)}function writeCatalogCache(e,t,r,o){const s=o||(()=>{});if(!r)return!1;try{fs.mkdirSync(path.dirname(e),{recursive:!0});const o={version:1,tokenFingerprint:r,savedAt:(new Date).toISOString(),services:sanitizeServicesForCache(t)},s=`${e}.${process.pid}.tmp`;return fs.writeFileSync(s,JSON.stringify(o),{mode:384}),fs.renameSync(s,e),!0}catch(e){return s(`[catalogCache] write failed: ${e.message}`),!1}}function isCloudProxyService(e,t){if(!e||"function"!=typeof e.keys)return!1;const r=`${t}__`;for(const t of e.keys())if("string"==typeof t&&t.startsWith(r))return!0;return!1}if(module.exports={ChildMcpProcess:ChildMcpProcess,ChildMcpManager:ChildMcpManager,BlueLampServer:BlueLampServer,isCloudProxyService:isCloudProxyService,buildCatalogRegistry:buildCatalogRegistry,buildGatewayToolList:buildGatewayToolList,catalogEntryToTool:catalogEntryToTool,diffChildToolsAgainstCatalog:diffChildToolsAgainstCatalog,decideCatalogReconciliation:decideCatalogReconciliation,sanitizeServicesForCache:sanitizeServicesForCache,catalogCacheFingerprint:catalogCacheFingerprint,readCatalogCache:readCatalogCache,writeCatalogCache:writeCatalogCache,CATALOG_CACHE_FILE:CATALOG_CACHE_FILE,CATALOG_CACHE_VERSION:1,stableJson:stableJson,sliceTailPreservingSurrogates:sliceTailPreservingSurrogates,CHILD_READY_TIMEOUT_MS:3e4,CHILD_NOT_READY_TEXT:CHILD_NOT_READY_TEXT},require.main===module){const e=new BlueLampServer;let t=!1;const r=r=>{if(!t){t=!0;try{log(`[Gateway] Shutdown (${r}) - cleaning up children...`)}catch(e){}if(e&&e.childManager&&e.childManager.children)for(const[t,r]of e.childManager.children)try{try{log(`[Gateway] Stopping child: ${t}`)}catch(e){}r&&"external"===r.type&&r.process&&"function"==typeof r.process.stop&&r.process.stop()}catch(e){}setTimeout(()=>process.exit(0),200).unref()}};process.on("SIGTERM",()=>r("SIGTERM")),process.on("SIGINT",()=>r("SIGINT")),process.on("SIGHUP",()=>r("SIGHUP")),process.stdin.on("end",()=>r("stdin-end")),process.stdin.on("close",()=>r("stdin-close")),process.on("exit",()=>{if(e&&e.childManager&&e.childManager.children)for(const[t,r]of e.childManager.children)try{r&&r.process&&r.process.process&&"function"==typeof r.process.process.kill&&r.process.process.kill("SIGKILL")}catch(e){}}),e.run().catch(e=>{log(`[Gateway] Fatal: ${e.message}`),process.exit(1)})}
|
|
2
|
+
const{Server:Server}=require("@modelcontextprotocol/sdk/server/index.js"),{StdioServerTransport:StdioServerTransport}=require("@modelcontextprotocol/sdk/server/stdio.js"),{CallToolRequestSchema:CallToolRequestSchema,ListToolsRequestSchema:ListToolsRequestSchema}=require("@modelcontextprotocol/sdk/types.js"),{getPromptFromPortal:getPromptFromPortal,listAvailableMcps:listAvailableMcps,downloadMcpPackage:downloadMcpPackage,getGatewayConfig:getGatewayConfig,searchMcpServices:searchMcpServices,installMcpService:installMcpService,setServiceDeactivated:setServiceDeactivated,uninstallMcpService:uninstallMcpService,updateMcpEnv:updateMcpEnv,callMcpToolProxy:callMcpToolProxy}=require("./portal-api-client.cjs"),{requireFromPackage:requireFromPackage}=require("../requireFromPackage.cjs"),{logger:logger}=requireFromPackage(require,"../../utils/logger"),{t:t,setLanguage:setLanguage}=requireFromPackage(require,"../../i18n");requireFromPackage(require,"../../lib/connectResilience").applyConnectResilience(require("node:net"));{const{SYS_PROMPT_ENV:e,isOwnSysPromptPath:t}=requireFromPackage(require,"../../lib/sysPromptHandoff"),r=process.env[e];if(t(r,require("node:os").tmpdir()))try{require("node:fs").unlinkSync(r)}catch{}}const{describeTokenForDebug:describeTokenForDebug}=requireFromPackage(require,"../../lib/debugRedaction"),{collectUpdateNotices:collectUpdateNotices,buildUpdateNoticeText:buildUpdateNoticeText}=require("./mcp-version-advisor.cjs"),{installPackageFiles:installPackageFiles,formatInstallFailure:formatInstallFailure,runProductionNpmInstall:runProductionNpmInstall}=require("./install-package.cjs"),spawn=require("cross-spawn"),{StringDecoder:StringDecoder}=require("string_decoder"),path=require("path"),fs=require("fs"),os=require("os"),dotenvLocalPath=path.join(__dirname,"../../.env.local"),dotenvPath=path.join(__dirname,"../../.env");fs.existsSync(dotenvLocalPath)?(require("dotenv").config({path:dotenvLocalPath}),logger.debug("[MCP Server] Loaded .env.local")):fs.existsSync(dotenvPath)&&(require("dotenv").config({path:dotenvPath}),logger.debug("[MCP Server] Loaded .env"));const KEYWORD=process.env.MUSUHI_KEYWORD||"BL-要件定義",{TokenManager:TokenManager}=requireFromPackage(require,"../../auth/TokenManager");function resolveToken(){return TokenManager.loadTokenStringSync()}const PORTAL_URL_ENV=process.env.BLUELAMP_PORTAL_API_BASE_URL||process.env.PORTAL_URL||"",isDevEnv=PORTAL_URL_ENV.includes("localhost"),PORTAL_URL=PORTAL_URL_ENV||(isDevEnv?"http://localhost:8430/api/bluelamp":"https://api.motohi.jp/api/bluelamp"),LANGUAGE=process.env.MUSUHI_LANGUAGE||"ja",CATALOG_CACHE_FILE=path.join(os.homedir(),".bluelamp-vscode","gateway-catalog.json"),CATALOG_CACHE_VERSION=1,CHILD_READY_TIMEOUT_MS=3e4,CHILD_NOT_READY_TEXT="起動中です。数秒後に再試行してください";setLanguage(LANGUAGE);const LOG_DIR=path.join(os.homedir(),".bluelamp-vscode","logs"),LOG_FILE=path.join(LOG_DIR,"mcp.log");function log(e){const t=`[${(new Date).toISOString()}] ${e}\n`;fs.appendFileSync(LOG_FILE,t),logger.info(`[MCP Server] ${e}`)}fs.existsSync(LOG_DIR)||fs.mkdirSync(LOG_DIR,{recursive:!0}),log(`MCP Server started - Log file: ${LOG_FILE}`),log("Environment variables:"),log(` KEYWORD: ${KEYWORD}`),log(` TOKEN (起動時スナップショット): ${describeTokenForDebug(resolveToken())}`),log(` PORTAL_URL: ${PORTAL_URL}`),log(` LANGUAGE: ${LANGUAGE}`);const LEGACY_REGISTRY=path.join(os.homedir(),".bluelamp","registry.json");if(fs.existsSync(LEGACY_REGISTRY))try{fs.unlinkSync(LEGACY_REGISTRY),log(`[startup] Legacy registry.json removed: ${LEGACY_REGISTRY}`)}catch(e){log(`[startup] Failed to remove legacy registry.json: ${e.message}`)}class ChildMcpProcess{constructor(e,t,r,o){this.serviceId=e,this.command=t,this.args=(r||[]).map(e=>"string"==typeof e?e.replace(/^~\//,os.homedir()+"/"):e),this.env={...process.env,...o},this.process=null,this.tools=[],this.requestId=0,this.pending=new Map,this.buffer="",this.stdoutDecoder=new StringDecoder("utf8"),this.stderrDecoder=new StringDecoder("utf8"),this.stderrTail="",this._decodersEnded=!1}async start(){return new Promise((e,t)=>{let r=!1;const o=(e,t)=>{r||(r=!0,clearTimeout(s),e(t))},s=setTimeout(()=>{o(t,new Error(`Timeout: "${this.serviceId}" failed to start within 90s`)),this.stop()},9e4);try{this.process=spawn(this.command,this.args,{env:this.env,stdio:["pipe","pipe","pipe"]}),this.process.on("error",e=>{o(t,new Error(`Spawn error for "${this.serviceId}": ${e.message}`))}),this.process.stdin.on("error",e=>{log(`[ChildMcp:${this.serviceId}:stdin] ${e.message}`)}),this.process.on("close",()=>{this._flushDecoders("close")}),this.process.on("exit",(e,r)=>{log(`[ChildMcp] "${this.serviceId}" exited (code=${e}, signal=${r})`);const s=this.stderrTail.trim();o(t,new Error(`"${this.serviceId}" exited before ready (code=${e}, signal=${r})${s?` | stderr: ${s}`:""}`)),this.pending.forEach(e=>e.reject(new Error("Process exited"))),this.pending.clear()}),this.process.on("close",()=>{if(this._decodersEnded)return;this._decodersEnded=!0;const e=this.stdoutDecoder.end(),t=this.stderrDecoder.end();e&&(this.buffer+=e,log(`[ChildMcp] "${this.serviceId}" stdout に半端なバイトが ${Buffer.byteLength(e)} バイト残っていた(子が多バイト文字の途中で終了)⇒ buffer へ流した。⛔ これは「壊れた文字」であって「失われた文字」ではない`)),t&&log(`[ChildMcp] "${this.serviceId}" stderr に半端なバイトが ${Buffer.byteLength(t)} バイト残っていた ⇒ ${JSON.stringify(t)}`)}),this.process.stderr.on("data",e=>{const t=this.stderrDecoder.write(e);this.stderrTail=sliceTailPreservingSurrogates(this.stderrTail+t,500),log(`[ChildMcp:${this.serviceId}:stderr] ${t.trim()}`)}),this.process.stdout.on("data",e=>{this.buffer+=this.stdoutDecoder.write(e),this._processBuffer()}),this._sendRequest("initialize",{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"bluelamp-gateway",version:"1.0.0"}}).then(e=>(log(`[ChildMcp] "${this.serviceId}" initialized: ${JSON.stringify(e.serverInfo||{})}`),this._sendNotification("notifications/initialized",{}),this._sendRequest("tools/list",{}))).then(t=>{const r=t.tools||[];this.tools=r.map(e=>({...e,name:`${this.serviceId}__${e.name}`,_originalName:e.name})),log(`[ChildMcp] "${this.serviceId}" ready: ${this.tools.length} tools`),o(e,this.tools)}).catch(e=>{o(t,e)})}catch(e){o(t,e)}})}async callTool(e,t){return await this._sendRequest("tools/call",{name:e,arguments:t})}_flushDecoders(e){for(const[t,r]of[["stdout",this.stdoutDecoder],["stderr",this.stderrDecoder]]){let o="";try{o=r.end()}catch(e){log(`[ChildMcp:${this.serviceId}:${t}] decoder.end() が落ちた: ${e.message}`);continue}o&&log(`[ChildMcp:${this.serviceId}:${t}] 🚨 未完の多バイト列を ${e} 時に検出: ${JSON.stringify(o)}(${o.length} 文字・⛔ 黙って捨てていない)`)}}stop(){this.process&&(this.process.kill(),this.process=null),this.pending.forEach(e=>e.reject(new Error("Process stopped"))),this.pending.clear()}_sendRequest(e,t){return new Promise((r,o)=>{const s=++this.requestId,i=JSON.stringify({jsonrpc:"2.0",id:s,method:e,params:t})+"\n";this.pending.set(s,{resolve:r,reject:o});try{this.process.stdin.write(i)}catch(e){this.pending.delete(s),o(new Error(`Write error: ${e.message}`))}})}_sendNotification(e,t){const r=JSON.stringify({jsonrpc:"2.0",method:e,params:t})+"\n";try{this.process.stdin.write(r)}catch(e){log(`[ChildMcp] Notification write error: ${e.message}`)}}_processBuffer(){const e=this.buffer.split("\n");this.buffer=e.pop();for(const t of e){const e=t.trim();if(e)try{const t=JSON.parse(e);if(t.id&&this.pending.has(t.id)){const e=this.pending.get(t.id);this.pending.delete(t.id),t.error?e.reject(new Error(`JSON-RPC error: ${JSON.stringify(t.error)}`)):e.resolve(t.result)}}catch(t){log(`[ChildMcp:${this.serviceId}] Parse error: ${t.message} | line: ${e.substring(0,200)}`)}}}}class ChildMcpManager{constructor(){this.children=new Map,this.starting=new Map}async activateExternal(e,t){const r=new ChildMcpProcess(e,t.command,t.args||[],t.env||{}),o=await r.start();return this.children.set(e,{type:"external",process:r,tools:o,service:t}),o}beginActivateExternal(e,t){const r=this.children.get(e);if(r)return Promise.resolve({ok:!0,tools:r.tools||[]});const o=this.starting.get(e);if(o)return o;const s=this.activateExternal(e,t).then(e=>({ok:!0,tools:e}),e=>({ok:!1,error:e})).then(t=>(this.starting.delete(e),t));return this.starting.set(e,s),s}isStarting(e){return this.starting.has(e)}async waitForReady(e,t){if(this.children.has(e))return{ready:!0};const r=this.starting.get(e);if(!r)return{ready:!1,reason:"unknown"};let o=null;const s=new Promise(e=>{o=setTimeout(()=>e({timedOut:!0}),t),"function"==typeof o.unref&&o.unref()});try{const e=await Promise.race([r,s]);return e.timedOut?{ready:!1,reason:"timeout"}:e.ok?{ready:!0}:{ready:!1,reason:"failed",error:e.error}}finally{o&&clearTimeout(o)}}deactivate(e){const t=this.children.get(e);t&&("external"===t.type&&t.process&&t.process.stop(),this.children.delete(e))}isActive(e){return this.children.has(e)}getActiveTools(){const e=[];for(const t of this.children.values())e.push(...t.tools||[]);return e}async callTool(e,t){for(const[r,o]of this.children)if("external"===o.type){const r=o.tools.find(t=>t.name===e);if(r)return{found:!0,result:await o.process.callTool(r._originalName,t)}}return{found:!1}}getActiveServiceIds(){return[...this.children.keys()]}}class BlueLampServer{constructor(){this.childManager=new ChildMcpManager,this.updateNoticeText=null,this._catalogTools=new Map,this._cloudProxyTools=new Map,this._catalogServices=new Map,this._needChildForSchema=new Set,this._catalogFromCache=!1,this._configRefresh=null,this.server=new Server({name:"bluelamp",version:"2.0.0"},{capabilities:{tools:{listChanged:!0}}}),this.setupHandlers()}checkAuth(){return resolveToken()?{ok:!0}:{ok:!1,error:"BlueLamp にログインしてください。ターミナルで `bluelamp1` を一度実行すると ~/.musuhi/portal-token.enc が作成されます。"}}buildToolList(){return buildGatewayToolList([{name:"load_knowledge",description:t("mcp.toolDescriptionLoadKnowledge"),annotations:{readOnlyHint:!0,idempotentHint:!0},inputSchema:{type:"object",properties:{keyword:{type:"string",description:t("mcp.keywordDescription")}},required:["keyword"]}},{name:"search_mcp",description:'キーワードでインストール済みMCPサービスを検索します。使いたい機能を自然言語で説明してください。(例: "ブラウザ操作", "メール送信", "画像生成")',inputSchema:{type:"object",properties:{query:{type:"string",description:"検索キーワード(自然言語OK)"}},required:["query"]}},{name:"activate_service",description:"deactivate_service で一覧から外した MCP を、道具一覧に戻します(deactivate を戻すだけ)。権限のある MCP は最初から一覧に出ているので、通常このツールを撃つ必要はありません。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"サービスID"},always_on:{type:"boolean",description:"後方互換のため受け付けますが、一覧の可否には使いません(無視されます)"}},required:["service_id"]}},{name:"deactivate_service",description:"指定した MCP を道具一覧から外します(deactivated=true)。次の接続でも一覧に出なくなります。権限は変わらないので activate_service でいつでも戻せます。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"サービスID"}},required:["service_id"]}},{name:"restart_service",description:"MCPサービスのプロセスを再起動します。コード更新や設定変更の反映に使用します。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"サービスID"}},required:["service_id"]}},{name:"unregister_service",description:"MCPサービスをレジストリから完全に削除します。プロセスも停止します。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"削除するサービスID"}},required:["service_id"]}},{name:"register_service",description:"新しいMCPサービスをゲートウェイに登録します。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:'サービスID(英数字、例: "microcms")'},name:{type:"string",description:"サービス表示名"},description:{type:"string",description:"サービスの説明"},tags:{type:"array",items:{type:"string"},description:"検索用タグ"},command:{type:"string",description:'実行コマンド("npx" or "node")'},args:{type:"array",items:{type:"string"},description:"コマンド引数"},env:{type:"object",description:"環境変数"}},required:["service_id","name","description","tags"]}},{name:"browse_mcp_store",description:"MCPストア(Portal)からインストール可能なMCP一覧を取得します。",inputSchema:{type:"object",properties:{},required:[]}},{name:"install_from_store",description:"MCP ストアから MCP を取得してこの機体に展開し、env と導入版をサーバに記録します。browse_mcp_store で見つけた mcp_id を指定してください。道具一覧に出るかどうかは権限で決まるので、権限の無い MCP をこれで一覧に出すことはできません(自分で作った MCP・共有された MCP は権限に含まれるため、何もしなくても一覧に出ます)。",inputSchema:{type:"object",properties:{mcp_id:{type:"string",description:"インストールするMCPのID"}},required:["mcp_id"]}},{name:"save_mcp_env",description:"MCPの環境変数(APIキー等)をサーバーに安全に保存します。ローカルには保存されません。次回起動時にサーバーから自動取得されます。解約時はアクセスできなくなります。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"MCP サービスID(例: heygen, gemini-image)"},env:{type:"object",description:'環境変数のキーと値(例: {"HEYGEN_API_KEY": "sk_xxx"})',additionalProperties:{type:"string"}}},required:["service_id","env"]}}],this._catalogTools,this.childManager.getActiveTools())}applyCatalog(e,t={}){const{catalog:r,cloudProxy:o,warnings:s,needChildForSchema:i}=buildCatalogRegistry(e);this._catalogTools=r,this._cloudProxyTools=o,this._catalogServices=new Map,this._needChildForSchema=new Set(i),this._catalogFromCache=!0===t.fromCache;for(const t of Array.isArray(e)?e:[])t&&t.mcpId&&this._catalogServices.set(t.mcpId,t);let n=0;for(const e of s)"no-input-schema"===e.reason?(n+=e.count,log(`[catalog] "${e.mcpId}" (${e.hostingType||"unknown"}): ${e.count}/${e.total} tools have no inputSchema — 一覧には出す(空 schema)。子を起こして実 schema で上書きする`)):log(`[catalog] "${e.mcpId}" (${e.hostingType||"unknown"}) skipped: ${e.reason}`);return n>0&&log(`[catalog] inputSchema 欠損の合計: ${n} 本 / ${this._needChildForSchema.size} service(一覧には出す。子を起こして実 schema で上書きする)`),log(`[catalog] ${r.size} tools registered from ${this._catalogServices.size} services (cloud_proxy: ${o.size})`),r}registerServiceInCatalog(e){const{catalog:t,cloudProxy:r,warnings:o}=buildCatalogRegistry([e]);for(const[e,r]of t)this._catalogTools.set(e,r);for(const[e,t]of r)this._cloudProxyTools.set(e,t);for(const e of o)log(`[catalog] "${e.mcpId}" (${e.hostingType||"unknown"}) skipped: ${e.reason}`);return e&&e.mcpId&&this._catalogServices.set(e.mcpId,e),t.size}unregisterServiceFromCatalog(e){const t=`${e}__`;let r=0;for(const e of[...this._catalogTools.keys()])e.startsWith(t)&&(this._catalogTools.delete(e),this._cloudProxyTools.delete(e),r++);return r}buildChildService(e,t){const r=path.join(os.homedir(),".claude","mcp-servers",e),o=t.entryPoint||"dist/index.js";return{name:t.name,description:t.description,type:"external",command:t.command||"node",args:t.args&&t.args.length?t.args:[path.join(r,o)],env:t.env||{}}}setupHandlers(){this.server.setRequestHandler(ListToolsRequestSchema,async()=>{const e=this.buildToolList(),t=this.childManager.getActiveServiceIds();return log(`[ListTools] ${e.length} tools (active: ${t.join(", ")||"none"})`),{tools:e}}),this.server.setRequestHandler(CallToolRequestSchema,async e=>{const{name:r,arguments:o}=e.params;log(`[CallTool] ${r}`);const s=resolveToken();if("load_knowledge"===r){const{keyword:e}=o;log(`[load_knowledge] keyword: ${e}`);try{const r=await getPromptFromPortal(e,s,LANGUAGE);if(!r||!r.success){const o=r?.error||t("mcp.knowledgeNotFound",{keyword:e});return{content:[{type:"text",text:`❌ ${t("mcp.portalApiFailed",{error:o})}`}],isError:!0}}const o="ja"!==LANGUAGE?"\n\n[Note] The user's preferred language is English.":"",i=this.updateNoticeText?`\n\n${this.updateNoticeText}`:"";return{content:[{type:"text",text:`✅ ${t("mcp.knowledgeLoadSuccess")}\n\n${r.knowledge}${o}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${t("mcp.knowledgeApplied")}${i}`}]}}catch(e){return log(`[load_knowledge] Exception: ${e.message}`),{content:[{type:"text",text:`❌ ${t("mcp.knowledgeLoadError",{error:e.message})}`}],isError:!0}}}if("search_mcp"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};if(!s)return{content:[{type:"text",text:"❌ 未認証です。MCPカタログの検索にはログインが必要です。"}],isError:!0};let t=[];try{const e=await searchMcpServices(o.query,s);if(!e.success)return{content:[{type:"text",text:`❌ MCPカタログ検索に失敗: ${e.error||"Unknown"}`}],isError:!0};t=(e.data||[]).map(e=>({id:e.mcp_id,service:{name:e.name,description:e.description,tags:[],alwaysOn:e.alwaysOn||!1},isInstalled:e.isInstalled||!1}))}catch(e){return log(`[search_mcp] Server search failed: ${e.message}`),{content:[{type:"text",text:`❌ MCPカタログ検索に失敗: ${e.message}`}],isError:!0}}if(0===t.length)return{content:[{type:"text",text:`🔍 「${o.query}」に一致するサービスはありません。\n\n別のキーワードで試すか、browse_mcp_storeでカタログを確認してください。`}]};let r=`🔍 「${o.query}」の検索結果(${t.length}件)\n\n`;for(const e of t.slice(0,10)){const t=e.id,o=e.service,s=this.childManager.isActive(t),i=!0===o.deactivated,n=e.isInstalled?"":" [未インストール]",a=i?" [一覧から外し中]":"";r+=`${i?"🚫":s?"🟢":"⚪"} **${o.name}**${a}${n} (id: \`${t}\`)\n`,r+=` ${o.description}\n\n`}return r+="一覧に戻す: activate_service | ストアから導入: install_from_store",{content:[{type:"text",text:r}]}}if("activate_service"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};const{service_id:t}=o,r=await getGatewayConfig(s);if(!r.success)return{content:[{type:"text",text:`❌ サーバー設定の取得に失敗: ${r.error||"Unknown"}`}],isError:!0};const i=(r.data?.services||[]).find(e=>e.mcpId===t);if(!i)return{content:[{type:"text",text:`❌ サービス "${t}" はあなたの権限の中にありません(権限のある MCP は何もしなくても一覧に出ます)。`}],isError:!0};const n=await setServiceDeactivated(t,!1,s);if(!n.success)return{content:[{type:"text",text:`❌ "${t}" を一覧に戻せませんでした: ${n.error||"Unknown"}`}],isError:!0};log(`[activate] "${t}" deactivated=false (一覧に戻した)`);const a=this.registerServiceInCatalog({...i,deactivated:!1});if(0===a)return{content:[{type:"text",text:`❌ "${t}" には目録にツール定義がありません(tools が空)。`}],isError:!0};if("cloud_proxy"===i.hostingType){log(`[activate] "${t}" (cloud_proxy) → ${a} proxy tools registered`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[activate] notification error: ${e.message}`)}return{content:[{type:"text",text:`✅ "${i.name}" を一覧に戻しました(cloud_proxy ツール${a}個)`}]}}if(this.childManager.isActive(t)){try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[activate] notification error: ${e.message}`)}return{content:[{type:"text",text:`✅ "${t}" を一覧に戻しました(子プロセスは既に起動済み)。`}]}}log(`[activate] "${t}" (${i.hostingType}) → ${a} catalog tools restored. Sending list_changed.`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[activate] notification error: ${e.message}`)}return{content:[{type:"text",text:`✅ "${i.name}" を一覧に戻しました(ツール${a}個)。最初に呼ばれた時に自動で起動します。`}]}}if("deactivate_service"===r){const{service_id:e}=o;if(!s)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};const t=await setServiceDeactivated(e,!0,s);if(!t.success)return{content:[{type:"text",text:`❌ "${e}" を一覧から外せませんでした: ${t.error||"Unknown"}`}],isError:!0};log(`[deactivate] "${e}" deactivated=true (一覧から外した)`);const r=this.unregisterServiceFromCatalog(e);r>0&&log(`[deactivate] "${e}" → ${r} catalog tools removed`),this._catalogServices.delete(e);let i=!1;if(this.childManager.isActive(e)&&(this.childManager.deactivate(e),log(`[deactivate] "${e}" child process stopped`),i=!0),r>0||i)try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[deactivate] notification error: ${e.message}`)}let n=`✅ "${e}" を道具一覧から外しました(deactivated=true)。`;return r>0&&(n+=` ツール${r}個を一覧から削除しました。`),i&&(n+=" 子プロセスを停止しました。"),n+=" 次の接続でも一覧に出ません。権限は変わっていないので activate_service で戻せます。",{content:[{type:"text",text:n}]}}if("restart_service"===r){const{service_id:e}=o,t=this.childManager.children.get(e),r=t?.service;if(!r)return isCloudProxyService(this._cloudProxyTools,e)?{content:[{type:"text",text:`ℹ️ "${e}" は cloud_proxy 方式のため、ローカルの再起動対象ではありません(子プロセスを持ちません)。\nサーバ側のツール更新は deploy で全利用者に反映されるので、再起動は不要です。\nツール一覧を取り直したいだけなら deactivate_service → activate_service を実行してください。`}]}:{content:[{type:"text",text:`❌ "${e}" の子プロセスは現在動いていません。道具を呼べば自動で起動します(restart は起動済みの子にだけ効きます)。`}],isError:!0};let i,n=r;if(s)try{const t=await getGatewayConfig(s);if(t.success){const o=(t.data?.services||[]).find(t=>t.mcpId===e);o&&o.env&&(n={...r,env:o.env},log(`[restart] "${e}" env refreshed from server (keys=${Object.keys(o.env).join(",")||"none"})`))}}catch(e){log(`[restart] env refresh failed (using cached): ${e.message}`)}this.childManager.deactivate(e),log(`[restart] "${e}" stopped`);try{i=await this.childManager.activateExternal(e,n)}catch(t){return log(`[restart] Failed "${e}": ${t.message}`),{content:[{type:"text",text:`❌ "${e}" の再起動に失敗しました: ${t.message}`}],isError:!0}}log(`[restart] "${e}" → ${i.length} tools. Sending list_changed.`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[restart] notification error: ${e.message}`)}const a=i.map(e=>e.name).join(", ");return{content:[{type:"text",text:`🔄 "${n.name}" を再起動しました(ツール${i.length}個)\n\nツール: ${a||"(なし)"}`}]}}if("unregister_service"===r){const{service_id:e}=o;if(!s)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};this.childManager.isActive(e)&&(this.childManager.deactivate(e),log(`[unregister] "${e}" stopped`));const t=await uninstallMcpService(e,s);if(!t.success)return{content:[{type:"text",text:`❌ "${e}" のサーバー削除に失敗: ${t.error||"Unknown"}`}],isError:!0};log(`[unregister] "${e}" uninstalled from server`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[unregister] notification error: ${e.message}`)}return{content:[{type:"text",text:`🗑️ "${e}" を削除しました。プロセスも停止済みです。`}]}}if("register_service"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};const{service_id:t,env:r}=o;if(!s)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};const i=r||{},n=await installMcpService(t,!1,s,i);return n.success?(log(`[register] "${t}" installed on server`),{content:[{type:"text",text:`✅ サービス "${t}" をサーバーに登録しました。\n\n次のステップ:\n- save_mcp_env で APIキー等を設定\n- 次の接続で道具一覧に出ます(activate は不要です)`}]}):{content:[{type:"text",text:`❌ "${t}" の登録に失敗しました: ${n.error||"Unknown"}\n\n※ MCP はサーバーカタログに登録されている必要があります。browse_mcp_store で確認してください。`}],isError:!0}}if("save_mcp_env"===r){const e=this.checkAuth();if(!e.ok)return{content:[{type:"text",text:`🔒 ${e.error}`}],isError:!0};const{service_id:t,env:r}=o;if(!t||!r||0===Object.keys(r).length)return{content:[{type:"text",text:"❌ service_id と env(キー/値のペア)が必要です"}],isError:!0};log(`[save_mcp_env] ${t}: ${Object.keys(r).join(", ")}`);try{const e=await updateMcpEnv(t,r,s);if(!e.success)return{content:[{type:"text",text:`❌ サーバー保存失敗: ${e.error}`}],isError:!0};const o=Object.keys(r).join(", ");return e.warning?{content:[{type:"text",text:`🔐 環境変数をサーバーに保存しました\n\nサービス: ${t}\n保存キー: ${o}\n\n${e.warning}`}]}:{content:[{type:"text",text:`🔐 環境変数をサーバーに保存しました\n\nサービス: ${t}\n保存キー: ${o}\n\n※ ローカルには保存されません。次回起動時にサーバーから自動取得されます。`}]}}catch(e){return log(`[save_mcp_env] error: ${e.message}`),{content:[{type:"text",text:`❌ ${e.message}`}],isError:!0}}}if("browse_mcp_store"===r){log("[browse_mcp_store]");try{const e=await listAvailableMcps({},s,LANGUAGE);if(!e||!e.success)return{content:[{type:"text",text:`❌ MCPカタログの取得に失敗: ${e?.error||"Unknown"}`}],isError:!0};const t=e.data||[];if(0===t.length)return{content:[{type:"text",text:"📦 インストール可能なMCPはありません。"}]};let r=`📦 MCPストア一覧 (${t.length}件)\n\n`;return t.forEach(e=>{const t=e.isOfficial?" [公式]":"";r+=`**${e.name}**${t}\n`,r+=` ID: ${e.mcpId}\n`,r+=` ${e.description}\n`,r+=` ツール数: ${e.toolCount}\n\n`}),r+='\nインストールするには: install_from_store(mcp_id: "MCP_ID")',{content:[{type:"text",text:r}]}}catch(e){return log(`[browse_mcp_store] error: ${e.message}`),{content:[{type:"text",text:`❌ ${e.message}`}],isError:!0}}}if("install_from_store"===r){const{mcp_id:e}=o;log(`[install_from_store] mcp_id=${e}`);try{const t=await downloadMcpPackage(e,s);if(!t||!t.success)return{content:[{type:"text",text:`❌ MCPダウンロードに失敗: ${t?.error||"Unknown"}`}],isError:!0};const r=t.data,o=r.packageSource||{},i=r.files||[];let n=o.hostingType||o.hosting&&o.hosting.type||null;if(s){let t=!1;try{const r=await getGatewayConfig(s);if(r.success){const o=(r.data?.services||[]).find(t=>t.mcpId===e);o&&(t=!0===o.alwaysOn,n||(n=o.hostingType))}}catch(e){}const r=await installMcpService(e,t,s);if(!r.success)return{content:[{type:"text",text:`❌ サーバー登録失敗: ${r.error||"Unknown"}`}],isError:!0};log(`[install_from_store] Server sync OK: ${e} (alwaysOn=${t})`)}if("cloud_proxy"===n){log(`[install_from_store] "${e}" is cloud_proxy, skipping local extract`);let t=`✅ MCP「${o.name||e}」をインストールしました(cloud_proxy)\n\n`;return t+="☁ クラウド実行のためローカル展開は不要です\n",o.installNotes&&(t+=`\n📝 ${o.installNotes}\n`),t+="\n権限に含まれていれば、次の接続で道具一覧に出ます(activate_service を撃つ必要はありません)。",{content:[{type:"text",text:t}]}}if(0===i.length)return{content:[{type:"text",text:"❌ MCPパッケージにファイルが含まれていません。"}],isError:!0};const a=path.join(os.homedir(),".claude","mcp-servers",e),c=installPackageFiles({installDir:a,files:i,needsNpmInstall:!!o.needsNpmInstall,runNpmInstall:e=>runProductionNpmInstall(e)});if(!c.ok)return log(`[install_from_store] ${e} failed at ${c.stage}: ${c.error.message}`),{content:[{type:"text",text:formatInstallFailure(o.name||e,c)}],isError:!0};log(`[install_from_store] ${i.length} files written to ${a}`);let l=`✅ MCP「${o.name||e}」をインストールしました\n\n`;l+=`📁 インストール先: ${a}\n`,l+=`📄 ファイル数: ${i.length}\n`,o.installNotes&&(l+=`\n📝 ${o.installNotes}\n`);const d=(o.requiredEnvVars||[]).filter(e=>!process.env[e]);return d.length>0&&(l+="\n⚠️ 以下の環境変数を save_mcp_env で設定してください:\n",d.forEach(e=>{l+=` - ${e}\n`})),l+="\n権限に含まれていれば、次の接続で道具一覧に出ます(activate_service を撃つ必要はありません)。",{content:[{type:"text",text:l}]}}catch(e){return log(`[install_from_store] error: ${e.message}`),{content:[{type:"text",text:`❌ ${e.message}`}],isError:!0}}}if(this._cloudProxyTools&&this._cloudProxyTools.has(r)){const e=this._cloudProxyTools.get(r);log(`[callTool] Cloud proxy: ${r} → ${e.mcpId}/${e.toolName}`);try{const t=await callMcpToolProxy(e.mcpId,e.toolName,o,s);return t.success&&t.data?t.data.content?t.data:{content:[{type:"text",text:JSON.stringify(t.data,null,2)}]}:{content:[{type:"text",text:`❌ プロキシエラー: ${t.error||"Unknown"}`}],isError:!0}}catch(e){return log(`[callTool] Cloud proxy error "${r}": ${e.message}`),{content:[{type:"text",text:`❌ クラウドプロキシエラー: ${e.message}`}],isError:!0}}}try{const{found:e,result:t}=await this.childManager.callTool(r,o);if(e)return t&&t.content?t:{content:[{type:"text",text:String(t)}]}}catch(e){return log(`[callTool] Error proxying "${r}": ${e.message}`),{content:[{type:"text",text:`❌ ツール実行エラー: ${e.message}`}],isError:!0}}const i=this._catalogTools.get(r);if(i&&"cloud_proxy"!==i.hostingType){const e=i.mcpId;this._catalogFromCache&&this._configRefresh&&(log(`[callTool] "${e}" は目録キャッシュ由来。起動仕様の到着を待つ。`),await this._configRefresh),this.startChildOnDemand(e);const t=await this.childManager.waitForReady(e,3e4);if(!t.ready){if("failed"===t.reason){const r=t.error?.message||String(t.error||"Unknown");return log(`[callTool] "${e}" failed to start: ${r}`),{content:[{type:"text",text:`❌ "${e}" の起動に失敗しました: ${r}`}],isError:!0}}return"timeout"===t.reason?(log(`[callTool] "${e}" not ready within 30000ms for "${r}".`),{content:[{type:"text",text:CHILD_NOT_READY_TEXT}],isError:!0}):(log(`[callTool] "${e}" could not be started for "${r}" (reason=${t.reason}).`),{content:[{type:"text",text:`❌ "${e}" を起動できませんでした(設定が見つかりません)。`}],isError:!0})}try{const{found:t,result:s}=await this.childManager.callTool(r,o);if(t)return s&&s.content?s:{content:[{type:"text",text:String(s)}]};log(`[callTool] "${r}" is in catalog but child "${e}" does not have it.`)}catch(e){return log(`[callTool] Error proxying "${r}" after ready: ${e.message}`),{content:[{type:"text",text:`❌ ツール実行エラー: ${e.message}`}],isError:!0}}}return{content:[{type:"text",text:`❌ Unknown tool: ${r}`}],isError:!0}})}startChildrenForMissingSchema(){let e=0;for(const t of this._needChildForSchema){const r=this._catalogServices.get(t);if(r)if("cloud_proxy"!==r.hostingType){if(!this.childManager.isActive(t)&&!this.childManager.isStarting(t)){log(`[schemaChild] "${t}" (${r.hostingType}) を起こして実 schema を取りに行く`);try{this.startChildOnDemand(t),e++}catch(e){log(`[schemaChild] "${t}" の起動に失敗: ${e.message}(⛔ 席は落とさない)`)}}}else log(`[schemaChild] "${t}" は cloud_proxy のため子を起こせない。schema 欠損の道具は一覧に出ない。`)}return log(`[schemaChild] ${e} service(=目録に schema が無い道具を持つ service の数)`),e}async loadCatalog(e){if(!e)return log("[catalog] No token, starting with no catalog tools."),[];let t=[];try{const r=await getGatewayConfig(e);if(!r.success)return log(`[catalog] Server config failed: ${r.error||"Unknown"}. Starting with no services.`),[];t=r.data?.services||[],log(`[catalog] Server config: ${t.length} services`),this.applyCatalog(t),writeCatalogCache(CATALOG_CACHE_FILE,t,catalogCacheFingerprint(e),log)}catch(e){return log(`[catalog] Server config error: ${e.message}. Starting with no services.`),[]}try{const e=collectUpdateNotices(t);this.updateNoticeText=buildUpdateNoticeText(e);for(const t of e)"not-applicable"!==t.status&&log(`[versionCheck] ${t.mcpId}: ${t.status} (${t.reason}) catalog=${t.catalogVersion} local=${t.localVersion}`)}catch(e){log(`[versionCheck] skipped: ${e.message}`)}return t}startChildOnDemand(e){const t=this._catalogServices.get(e);if(!t||!0===t.deactivated)return null;if("cloud_proxy"===t.hostingType)return null;if(this.childManager.isActive(e))return null;const r=this.childManager.isStarting(e),o=this.childManager.beginActivateExternal(e,this.buildChildService(e,t));return r||log(`[startChild] "${e}" (${t.hostingType}) starting on demand...`),o.then(t=>this.reconcileChildWithCatalog(e,t)).catch(t=>log(`[startChild] "${e}" post-start error: ${t.message}`)),o}async reconcileChildWithCatalog(e,t){const r=decideCatalogReconciliation(this._catalogTools,e,t);if(!r.ready){const o=t&&t.error?`: ${t.error.message||t.error}`:"";return log(`[versionDrift] "${e}" not ready (${r.reason})${o} → 剪定も通知もしない`),r}if(!r.notify)return log(`[versionDrift] "${e}" matches catalog (${t.tools.length} tools) → no list_changed`),r;for(const e of r.prune)this._catalogTools.delete(e),this._cloudProxyTools.delete(e);const o=r.diff;log(`[versionDrift] "${e}" differs from catalog: +${o.added.length} -${o.removed.length} ~${o.changed.length} → list_changed`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[versionDrift] notification error: ${e.message}`)}return r}toolListSignature(){return stableJson(this.buildToolList().map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema})))}async refreshCatalogInBackground(e){const t=this.toolListSignature();try{await this.loadCatalog(e)}catch(e){return log(`[catalogRefresh] failed, keeping cached catalog: ${e.message}`),{notified:!1,reason:"failed"}}if(t===this.toolListSignature())return log("[catalogRefresh] server catalog matches cache → no list_changed"),{notified:!1,reason:"same"};log("[catalogRefresh] server catalog differs from cache → list_changed");try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[catalogRefresh] notification error: ${e.message}`)}return{notified:!0,reason:"diff"}}async run(){const e=resolveToken(),t=catalogCacheFingerprint(e),r=readCatalogCache(CATALOG_CACHE_FILE,t,log);r?(this.applyCatalog(r,{fromCache:!0}),log(`[catalogCache] ${this._catalogTools.size} tools from cache (HTTP は待たない)`)):(log("[catalogCache] no usable cache, waiting for server config"),await this.loadCatalog(e));const o=new StdioServerTransport;await this.server.connect(o),log(`[Gateway] BlueLamp Gateway v2.0.0 started. (${this._catalogTools.size} catalog tools available at connect)`),r?(this._configRefresh=this.refreshCatalogInBackground(e).catch(e=>(log(`[catalogRefresh] unexpected: ${e.message}`),{notified:!1,reason:"failed"})),this._configRefresh.then(()=>this.startChildrenForMissingSchema()).catch(e=>log(`[schemaChild] deferred start failed: ${e.message}`))):this.startChildrenForMissingSchema();const s=resolveToken();if(KEYWORD&&s){log(`[Gateway] Auto-loading knowledge for keyword: ${KEYWORD}`);const e=await getPromptFromPortal(KEYWORD,s,LANGUAGE);e&&e.success?log(`[Gateway] Auto-load successful: ${e.title}`):log(`[Gateway] Auto-load failed for keyword: ${KEYWORD}`)}}}function buildCatalogRegistry(e){const t=new Map,r=new Map,o=[],s=[];for(const i of Array.isArray(e)?e:[]){if(!i||"string"!=typeof i.mcpId||!i.mcpId)continue;const e=i.hostingType||null;if(!0===i.deactivated)continue;const n=Array.isArray(i.tools)?i.tools:[];if(0===n.length){o.push({mcpId:i.mcpId,hostingType:e,reason:"no-tools"});continue}const a=[];for(const s of n){if(!s||"string"!=typeof s.name||!s.name){o.push({mcpId:i.mcpId,hostingType:e,reason:"unnamed-tool"});continue}s.inputSchema&&"object"==typeof s.inputSchema||a.push(s.name);const n=`${i.mcpId}__${s.name}`,c={name:n,mcpId:i.mcpId,toolName:s.name,hostingType:e,description:s.description,inputSchema:s.inputSchema};t.set(n,c),"cloud_proxy"===e&&r.set(n,{mcpId:i.mcpId,toolName:s.name,description:s.description,inputSchema:s.inputSchema})}a.length>0&&(o.push({mcpId:i.mcpId,hostingType:e,reason:"no-input-schema",count:a.length,total:n.length,tools:a}),s.push(i.mcpId))}return{catalog:t,cloudProxy:r,warnings:o,needChildForSchema:s}}function catalogEntryToTool(e){return{name:e.name,description:e.description||`[${e.mcpId}] ${e.toolName}`,inputSchema:e.inputSchema||{type:"object",properties:{}}}}function buildGatewayToolList(e,t,r){const o=new Map;for(const t of Array.isArray(e)?e:[])t&&t.name&&o.set(t.name,t);if(t&&"function"==typeof t.values)for(const e of t.values())o.has(e.name)||o.set(e.name,catalogEntryToTool(e));for(const e of Array.isArray(r)?r:[])e&&e.name&&o.set(e.name,e);return[...o.values()]}function sanitizeServicesForCache(e){return(Array.isArray(e)?e:[]).filter(e=>e&&"string"==typeof e.mcpId&&e.mcpId).map(e=>({mcpId:e.mcpId,name:e.name,description:e.description,hostingType:e.hostingType||null,deactivated:!0===e.deactivated,tools:(Array.isArray(e.tools)?e.tools:[]).filter(e=>e&&"string"==typeof e.name&&e.name).map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}))}))}function catalogCacheFingerprint(e){return e?require("crypto").createHash("sha256").update(String(e)).digest("hex").slice(0,16):null}function sliceTailPreservingSurrogates(e,t){const r=String(null==e?"":e);if(r.length<=t)return r;const o=r.slice(-t),s=o.charCodeAt(0);return s>=56320&&s<=57343?o.slice(1):o}function stableJson(e){return void 0===e?"null":null===e||"object"!=typeof e?JSON.stringify(e):Array.isArray(e)?`[${e.map(stableJson).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${stableJson(e[t])}`).join(",")}}`}function diffChildToolsAgainstCatalog(e,t,r){const o=`${t}__`,s=new Set;if(e&&"function"==typeof e.keys)for(const t of e.keys())"string"==typeof t&&t.startsWith(o)&&s.add(t);const i=[],n=[],a=new Set;for(const t of Array.isArray(r)?r:[]){if(!t||!t.name)continue;a.add(t.name);const r=e&&"function"==typeof e.get?e.get(t.name):void 0;r?stableJson(r.description)===stableJson(t.description)&&stableJson(r.inputSchema)===stableJson(t.inputSchema)||n.push(t.name):i.push(t.name)}const c=[...s].filter(e=>!a.has(e));return{added:i,removed:c,changed:n,hasDiff:i.length+c.length+n.length>0}}function decideCatalogReconciliation(e,t,r){if(!r||!0!==r.ok||!Array.isArray(r.tools))return{ready:!1,prune:[],notify:!1,reason:r&&r.reason||"not-ready",diff:null};const o=diffChildToolsAgainstCatalog(e,t,r.tools);return{ready:!0,prune:o.removed,notify:o.hasDiff,reason:"ready",diff:o}}function readCatalogCache(e,t,r){const o=r||(()=>{});if(!t)return null;let s,i;try{s=fs.readFileSync(e,"utf8")}catch(e){return"ENOENT"!==e.code&&o(`[catalogCache] read failed: ${e.message}`),null}try{i=JSON.parse(s)}catch(e){return o(`[catalogCache] broken JSON, ignoring: ${e.message}`),null}return i&&1===i.version?i.tokenFingerprint!==t?(o("[catalogCache] fingerprint mismatch (口座が違う), ignoring"),null):Array.isArray(i.services)?i.services:(o("[catalogCache] services が配列でない, ignoring"),null):(o(`[catalogCache] version mismatch (${i&&i.version}), ignoring`),null)}function writeCatalogCache(e,t,r,o){const s=o||(()=>{});if(!r)return!1;try{fs.mkdirSync(path.dirname(e),{recursive:!0});const o={version:1,tokenFingerprint:r,savedAt:(new Date).toISOString(),services:sanitizeServicesForCache(t)},s=`${e}.${process.pid}.tmp`;return fs.writeFileSync(s,JSON.stringify(o),{mode:384}),fs.renameSync(s,e),!0}catch(e){return s(`[catalogCache] write failed: ${e.message}`),!1}}function isCloudProxyService(e,t){if(!e||"function"!=typeof e.keys)return!1;const r=`${t}__`;for(const t of e.keys())if("string"==typeof t&&t.startsWith(r))return!0;return!1}if(module.exports={ChildMcpProcess:ChildMcpProcess,ChildMcpManager:ChildMcpManager,BlueLampServer:BlueLampServer,isCloudProxyService:isCloudProxyService,buildCatalogRegistry:buildCatalogRegistry,buildGatewayToolList:buildGatewayToolList,catalogEntryToTool:catalogEntryToTool,diffChildToolsAgainstCatalog:diffChildToolsAgainstCatalog,decideCatalogReconciliation:decideCatalogReconciliation,sanitizeServicesForCache:sanitizeServicesForCache,catalogCacheFingerprint:catalogCacheFingerprint,readCatalogCache:readCatalogCache,writeCatalogCache:writeCatalogCache,CATALOG_CACHE_FILE:CATALOG_CACHE_FILE,CATALOG_CACHE_VERSION:1,stableJson:stableJson,sliceTailPreservingSurrogates:sliceTailPreservingSurrogates,CHILD_READY_TIMEOUT_MS:3e4,CHILD_NOT_READY_TEXT:CHILD_NOT_READY_TEXT},require.main===module){const e=new BlueLampServer;let t=!1;const r=r=>{if(!t){t=!0;try{log(`[Gateway] Shutdown (${r}) - cleaning up children...`)}catch(e){}if(e&&e.childManager&&e.childManager.children)for(const[t,r]of e.childManager.children)try{try{log(`[Gateway] Stopping child: ${t}`)}catch(e){}r&&"external"===r.type&&r.process&&"function"==typeof r.process.stop&&r.process.stop()}catch(e){}setTimeout(()=>process.exit(0),200).unref()}};process.on("SIGTERM",()=>r("SIGTERM")),process.on("SIGINT",()=>r("SIGINT")),process.on("SIGHUP",()=>r("SIGHUP")),process.stdin.on("end",()=>r("stdin-end")),process.stdin.on("close",()=>r("stdin-close")),process.on("exit",()=>{if(e&&e.childManager&&e.childManager.children)for(const[t,r]of e.childManager.children)try{r&&r.process&&r.process.process&&"function"==typeof r.process.process.kill&&r.process.process.kill("SIGKILL")}catch(e){}}),e.run().catch(e=>{log(`[Gateway] Fatal: ${e.message}`),process.exit(1)})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const fs=require("fs"),path=require("path"),PRESERVE_ACROSS_SWAP=["node_modules"];function backupPathFor(e,t=new Date){return`${e}.bak-${t.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"Z")}`}function assertInsideInstallDir(e,t){const n=path.resolve(e),r=path.resolve(t);if(r!==n&&!r.startsWith(n+path.sep))throw new Error(`install: 展開先が installDir の外を指しています(${r})`);return r}function rmrf(e){fs.rmSync(e,{recursive:!0,force:!0})}function installPackageFiles(e){const{installDir:t,files:n,needsNpmInstall:r=!1,runNpmInstall:s,now:a}=e,o=fs.existsSync(t),c=o?backupPathFor(t,a):null;o&&fs.renameSync(t,c);const i=(e,n)=>{try{rmrf(t)}catch(e){}if(!c)return{ok:!1,rolledBack:!1,backupPath:null,stage:e,error:n};try{return fs.renameSync(c,t),{ok:!1,rolledBack:!0,backupPath:null,stage:e,error:n}}catch(t){return{ok:!1,rolledBack:!1,restoreFailed:!0,backupPath:c,stage:e,error:new Error(`${n.message} / さらに旧版の復元にも失敗: ${t.message}`)}}};let l=!1;try{fs.mkdirSync(t,{recursive:!0});for(const e of n){const n=assertInsideInstallDir(t,path.join(t,e.path));if(fs.mkdirSync(path.dirname(n),{recursive:!0}),fs.writeFileSync(n,e.content,"utf-8"),"number"==typeof e.mode)try{fs.chmodSync(n,511&e.mode)}catch(e){}}if(c&&!r)for(const e of PRESERVE_ACROSS_SWAP){const n=path.join(c,e),r=path.join(t,e);fs.existsSync(n)&&!fs.existsSync(r)&&(fs.renameSync(n,r),l=!0)}}catch(e){return i("extract",e)}if(r&&fs.existsSync(path.join(t,"package.json")))try{if("function"!=typeof s)throw new Error("runNpmInstall が渡されていません");s(t)}catch(e){return i("npm-install",e)}if(c)try{rmrf(c)}catch(e){}return{ok:!0,installDir:t,fileCount:n.length,restoredNodeModules:l}}function formatInstallFailure(e,t){const n="npm-install"===t.stage?"npm install":"ファイル展開";return t.restoreFailed?`❌ MCP「${e}」の${n}に失敗し、**旧版の復元にも失敗しました**。\n🚨 この機体の ${e} は壊れています。退避は次の場所に在ります:\n ${t.backupPath}\n詳細: ${t.error.message}`:t.rolledBack?`❌ MCP「${e}」の${n}に失敗したため、**旧版へ戻しました**(機体は元の状態です)。\n詳細: ${t.error.message}`:`❌ MCP「${e}」の${n}に失敗しました(新規インストールのため、作りかけは削除しました)。\n詳細: ${t.error.message}`}function withoutDevDependencies(e){const t=JSON.parse(e);return Object.prototype.hasOwnProperty.call(t,"devDependencies")?(delete t.devDependencies,`${JSON.stringify(t,null,2)}\n`):e}const PRODUCTION_NPM_INSTALL="npm install --omit=dev --no-audit --no-fund";function runProductionNpmInstall(e,{exec:t=require("child_process").execSync}={}){const n=path.join(e,"package.json"),r=fs.readFileSync(n,"utf-8"),s=withoutDevDependencies(r);s!==r&&fs.writeFileSync(n,s,"utf-8")
|
|
1
|
+
"use strict";const fs=require("fs"),path=require("path"),PRESERVE_ACROSS_SWAP=["node_modules"];function backupPathFor(e,t=new Date){return`${e}.bak-${t.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"Z")}`}function assertInsideInstallDir(e,t){const n=path.resolve(e),r=path.resolve(t);if(r!==n&&!r.startsWith(n+path.sep))throw new Error(`install: 展開先が installDir の外を指しています(${r})`);return r}function rmrf(e){fs.rmSync(e,{recursive:!0,force:!0})}function installPackageFiles(e){const{installDir:t,files:n,needsNpmInstall:r=!1,runNpmInstall:s,now:a}=e,o=fs.existsSync(t),c=o?backupPathFor(t,a):null;o&&fs.renameSync(t,c);const i=(e,n)=>{try{rmrf(t)}catch(e){}if(!c)return{ok:!1,rolledBack:!1,backupPath:null,stage:e,error:n};try{return fs.renameSync(c,t),{ok:!1,rolledBack:!0,backupPath:null,stage:e,error:n}}catch(t){return{ok:!1,rolledBack:!1,restoreFailed:!0,backupPath:c,stage:e,error:new Error(`${n.message} / さらに旧版の復元にも失敗: ${t.message}`)}}};let l=!1;try{fs.mkdirSync(t,{recursive:!0});for(const e of n){const n=assertInsideInstallDir(t,path.join(t,e.path));if(fs.mkdirSync(path.dirname(n),{recursive:!0}),fs.writeFileSync(n,e.content,"utf-8"),"number"==typeof e.mode)try{fs.chmodSync(n,511&e.mode)}catch(e){}}if(c&&!r)for(const e of PRESERVE_ACROSS_SWAP){const n=path.join(c,e),r=path.join(t,e);fs.existsSync(n)&&!fs.existsSync(r)&&(fs.renameSync(n,r),l=!0)}}catch(e){return i("extract",e)}if(r&&fs.existsSync(path.join(t,"package.json")))try{if("function"!=typeof s)throw new Error("runNpmInstall が渡されていません");s(t)}catch(e){return i("npm-install",e)}if(c)try{rmrf(c)}catch(e){}return{ok:!0,installDir:t,fileCount:n.length,restoredNodeModules:l}}function formatInstallFailure(e,t){const n="npm-install"===t.stage?"npm install":"ファイル展開";return t.restoreFailed?`❌ MCP「${e}」の${n}に失敗し、**旧版の復元にも失敗しました**。\n🚨 この機体の ${e} は壊れています。退避は次の場所に在ります:\n ${t.backupPath}\n詳細: ${t.error.message}`:t.rolledBack?`❌ MCP「${e}」の${n}に失敗したため、**旧版へ戻しました**(機体は元の状態です)。\n詳細: ${t.error.message}`:`❌ MCP「${e}」の${n}に失敗しました(新規インストールのため、作りかけは削除しました)。\n詳細: ${t.error.message}`}function withoutDevDependencies(e){const t=JSON.parse(e);return Object.prototype.hasOwnProperty.call(t,"devDependencies")?(delete t.devDependencies,`${JSON.stringify(t,null,2)}\n`):e}const PRODUCTION_NPM_INSTALL="npm install --omit=dev --no-audit --no-fund",NPM_INSTALL_TIMEOUT_MS=9e5;function runProductionNpmInstall(e,{exec:t=require("child_process").execSync}={}){const n=path.join(e,"package.json"),r=fs.readFileSync(n,"utf-8"),s=withoutDevDependencies(r);s!==r&&fs.writeFileSync(n,s,"utf-8");try{t(PRODUCTION_NPM_INSTALL,{cwd:e,timeout:9e5,stdio:"pipe"})}catch(e){if(e&&"ETIMEDOUT"===e.code)throw new Error(`npm install が 15 分で終わらず打ち切りました(網が極端に遅いか npm が固まった見込み)。撃ち直すと、取得済みの分は npm の置き場から使われます。(${e.message})`);throw e}}module.exports={installPackageFiles:installPackageFiles,formatInstallFailure:formatInstallFailure,backupPathFor:backupPathFor,PRESERVE_ACROSS_SWAP:PRESERVE_ACROSS_SWAP,withoutDevDependencies:withoutDevDependencies,runProductionNpmInstall:runProductionNpmInstall,PRODUCTION_NPM_INSTALL:PRODUCTION_NPM_INSTALL,NPM_INSTALL_TIMEOUT_MS:9e5};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const fetch=(...t)=>import("node-fetch").then(({default:e})=>e(...t)),https=require("https"),fs=require("fs"),path=require("path"),os=require("os"),{requireFromPackage:requireFromPackage}=require("../requireFromPackage.cjs"),{logger:logger}=requireFromPackage(require,"../../utils/logger"),{t:t}=requireFromPackage(require,"../../i18n"),PORTAL_URL_ENV=process.env.BLUELAMP_PORTAL_API_BASE_URL||process.env.PORTAL_URL||"",isDevEnvironment=PORTAL_URL_ENV.includes("localhost"),httpsAgent=new https.Agent({rejectUnauthorized:!isDevEnvironment}),PORTAL_URL=PORTAL_URL_ENV||(isDevEnvironment?"http://localhost:8430/api/bluelamp":"https://api.motohi.jp/api/bluelamp"),LOG_DIR=path.join(os.homedir(),".bluelamp-vscode","logs"),LOG_FILE=path.join(LOG_DIR,"mcp.log");function log(t){const e=`[${(new Date).toISOString()}] ${t}\n`;fs.appendFileSync(LOG_FILE,e),logger.debug(t)}let _cliVersion;function getCliVersion(){if(void 0!==_cliVersion)return _cliVersion;let t=__dirname;for(let e=0;e<6;e++){try{const e=require(path.join(t,"package.json"));if(e&&e.version&&e.bin&&Object.keys(e.bin).some(t=>/^[a-z]+\d+$/.test(t)))return _cliVersion=e.version,_cliVersion}catch(t){}const e=path.dirname(t);if(e===t)break;t=e}return _cliVersion=null,_cliVersion}async function getPromptFromPortal(e,o,r="ja"){try{if(log(`[portal-api-client] getPromptFromPortal called with keyword: ${e}, lang: ${r}, token: ${o?"present":"missing"}`),!o)return log("[portal-api-client] No authentication token provided"),{success:!1,error:t("mcp.noAuthToken")};const s=process.env.MUSUHI_TENANT_ID||"",a=encodeURIComponent(e);let n=`${PORTAL_URL}/cli/prompts/knowledge-injection/${a}?lang=${r}`;s&&(n+=`&tenant_id=${encodeURIComponent(s)}`),log(`[portal-api-client] Direct keyword search: ${n}`),log("[portal-api-client] About to call fetch...");const c=await fetch(n,{method:"GET",headers:{"X-CLI-Token":o,...getCliVersion()?{"X-CLI-Version":getCliVersion()}:{},...s?{"X-Tenant-ID":s}:{},"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:n.startsWith("https")?httpsAgent:void 0});if(log("[portal-api-client] Fetch completed"),log(`[portal-api-client] Response status: ${c.status}`),log(`[portal-api-client] Response headers: ${JSON.stringify(Object.fromEntries(c.headers.entries()))}`),!c.ok){log(`[portal-api-client] Portal API error: ${c.status}`);const e=await c.text();log(`[portal-api-client] Error response: ${e}`);let o=t("mcp.portalApiError",{status:c.status}),r=null;try{const t=JSON.parse(e);t.message&&(o=t.message),t.code&&(r=t.code)}catch(t){}return{success:!1,error:o,status:c.status,code:r}}const i=await c.json();if(log("[portal-api-client] Direct keyword search completed successfully"),!i.success)return log("[portal-api-client] API returned success: false"),{success:!1,error:i.message||t("mcp.apiReturnedError")};const l=i.data;if(!l)return log(`[portal-api-client] No prompt found for keyword: ${e}`),{success:!1,error:t("mcp.promptNotFoundForKeyword",{keyword:e})};if(log(`[portal-api-client] Found prompt: ${l.title}`),l.content||l.text)return{success:!0,knowledge:l.content||l.text,title:l.title,tags:l.tags||[]};try{const t=`${PORTAL_URL}/cli/prompts/${l.id}`;log(`[portal-api-client] Fetching prompt details from: ${t}`);const e=await fetch(t,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:httpsAgent});if(e.ok){const t=await e.json();if(t.success&&t.data?.prompt?.content)return log("[portal-api-client] Successfully fetched prompt content"),{success:!0,knowledge:t.data.prompt.content,title:l.title||l.name,tags:l.tags}}}catch(t){log(`[portal-api-client] Error fetching prompt details: ${t.message}`)}return{success:!0,knowledge:l.description||`# ${l.title}\n\n${t("mcp.knowledgeLoadError",{error:"detail fetch failed"})}`,title:l.title||l.name,tags:l.tags}}catch(e){return log(`[portal-api-client] FATAL ERROR in getPromptFromPortal: ${e.message}`),log(`[portal-api-client] Error stack: ${e.stack}`),logger.error(`Failed to get prompt from Portal: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function getSlotConfig(t,e){try{if(!e)return log("[portal-api-client] getSlotConfig: No token"),{ok:!1,reason:"no-token"};const o=process.env.MUSUHI_TENANT_ID||"",r=`${PORTAL_URL}/cli/slot-config/${t}`;log(`[portal-api-client] Fetching slot config: ${r}${o?` (tenant=${o})`:""}`);const s=await fetch(r,{method:"GET",headers:{"X-CLI-Token":e,...getCliVersion()?{"X-CLI-Version":getCliVersion()}:{},...o?{"X-Tenant-ID":o}:{},"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){log(`[portal-api-client] Slot config API error: ${s.status}`);let t=null;try{const e=await s.json();t=e?.error?.message||e?.message||null}catch{t=null}return 404===s.status?{ok:!1,reason:"not-configured",status:404,message:t}:403===s.status?{ok:!1,reason:"rank",status:403,message:t}:{ok:!1,reason:"http",status:s.status,message:t}}const a=await s.json();return a.success&&a.data?.keyword?(log(`[portal-api-client] Slot ${t}: custom keyword "${a.data.keyword}"`),{ok:!0,keyword:a.data.keyword}):(log(`[portal-api-client] Slot ${t}: no custom config`),{ok:!0,keyword:null})}catch(t){return log(`[portal-api-client] getSlotConfig error: ${t.message}`),{ok:!1,reason:"network",status:null,message:t.message}}}async function reportViolation(e,o,r,s){try{if(log(`[portal-api-client] reportViolation called: severity=${o}`),!r)return{success:!1,error:t("mcp.violationNoToken")};const a=`${PORTAL_URL}/cli/report-violation`,n={reason:e,severity:o};s&&(n.userMessage=s);const c=await fetch(a,{method:"POST",headers:{"X-CLI-Token":r,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:a.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(n)});if(!c.ok){const e=await c.text();return log(`[portal-api-client] reportViolation error: ${c.status} ${e}`),{success:!1,error:t("mcp.violationApiError",{status:c.status})}}const i=await c.json();return log(`[portal-api-client] reportViolation result: action=${i.action}, count=${i.violationCount}`),i}catch(e){return log(`[portal-api-client] reportViolation FATAL: ${e.message}`),{success:!1,error:t("mcp.violationCommunicationError",{error:e.message})}}}async function listAvailableMcps(e={},o,r="ja"){try{if(log(`[portal-api-client] listAvailableMcps called: ${JSON.stringify(e)}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const s=new URLSearchParams;e.search&&s.set("search",e.search),e.limit&&s.set("limit",String(e.limit)),s.set("lang",r);const a=`${PORTAL_URL}/mcp/catalog?${s.toString()}`;log(`[portal-api-client] Fetching MCP catalog: ${a}`);const n=await fetch(a,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:a.startsWith("https")?httpsAgent:void 0});if(!n.ok){const e=await n.text();log(`[portal-api-client] listAvailableMcps error: ${n.status} ${e}`);let o=t("mcp.listMcpsFailed",{error:`HTTP ${n.status}`});try{const t=JSON.parse(e);t.message&&(o=t.message)}catch(t){}return{success:!1,error:o}}const c=await n.json();return log(`[portal-api-client] listAvailableMcps: ${c.data?.length||0} MCPs found`),{success:!0,data:c.data||[]}}catch(e){return log(`[portal-api-client] listAvailableMcps FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function getMcpInfo(e,o,r="ja"){try{if(log(`[portal-api-client] getMcpInfo called: mcpId=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const s=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/details?lang=${r}`;log(`[portal-api-client] Fetching MCP info: ${s}`);const a=await fetch(s,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0});if(!a.ok){const e=await a.text();log(`[portal-api-client] getMcpInfo error: ${a.status} ${e}`);let o=t("mcp.getMcpInfoFailed",{error:`HTTP ${a.status}`});try{const t=JSON.parse(e);t.message&&(o=t.message)}catch(t){}return{success:!1,error:o}}const n=await a.json();return log(`[portal-api-client] getMcpInfo: found ${n.data?.name||e}`),{success:!0,data:n.data}}catch(e){return log(`[portal-api-client] getMcpInfo FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function callMcpTool(e,o,r,s){try{if(log(`[portal-api-client] callMcpTool called: mcpId=${e}, tool=${o}`),!s)return{success:!1,error:t("mcp.noAuthToken")};const a=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/call`,n={tool_name:o,arguments:r};log(`[portal-api-client] Calling MCP tool: ${a}`);const c=await fetch(a,{method:"POST",headers:{"X-CLI-Token":s,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:a.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(n)});if(!c.ok){const e=await c.text();log(`[portal-api-client] callMcpTool error: ${c.status} ${e}`);let o=t("mcp.callToolFailed",{error:`HTTP ${c.status}`});try{const t=JSON.parse(e);t.message&&(o=t.message)}catch(t){}return{success:!1,error:o}}const i=await c.json();return log("[portal-api-client] callMcpTool: success"),{success:!0,data:i.data}}catch(e){return log(`[portal-api-client] callMcpTool FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function scanMcpCode(e,o){try{if(log("[portal-api-client] scanMcpCode called"),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/scan`,s=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({code:e})});if(!s.ok){const t=await s.text();return log(`[portal-api-client] scanMcpCode error: ${s.status} ${t}`),{success:!1,error:`HTTP ${s.status}`}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] scanMcpCode FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function deployUserMcp(e,o){try{if(log(`[portal-api-client] deployUserMcp called: name=${e.name}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/deploy`,s=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(e)});if(!s.ok){const t=await s.text();log(`[portal-api-client] deployUserMcp error: ${s.status} ${t}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);if(o.error&&(e=o.error),o.details)return{success:!1,error:e,details:o.details}}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] deployUserMcp FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function listUserCreatedMcps(e){try{if(log("[portal-api-client] listUserCreatedMcps called"),!e)return{success:!1,error:t("mcp.noAuthToken")};const o=`${PORTAL_URL}/mcp/user/me/created`,r=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data}:(await r.text(),log(`[portal-api-client] listUserCreatedMcps error: ${r.status}`),{success:!1,error:`HTTP ${r.status}`})}catch(e){return log(`[portal-api-client] listUserCreatedMcps FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function deleteUserMcp(e,o){try{if(log(`[portal-api-client] deleteUserMcp called: mcpId=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}`,s=await fetch(r,{method:"DELETE",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){const t=await s.text();log(`[portal-api-client] deleteUserMcp error: ${s.status}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);o.error&&(e=o.error)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] deleteUserMcp FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function listMyPrompts(e){try{if(log("[portal-api-client] listMyPrompts called"),!e)return{success:!1,error:t("mcp.noAuthToken")};const o=`${PORTAL_URL}/cli/my-agents`,r=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data||[]}:(await r.text(),log(`[portal-api-client] listMyPrompts error: ${r.status}`),{success:!1,error:`HTTP ${r.status}`})}catch(e){return log(`[portal-api-client] listMyPrompts FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function saveMyPrompt(e,o){try{if(log(`[portal-api-client] saveMyPrompt called: title=${e.title}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/cli/my-agents`,s=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(e)});if(!s.ok){const t=await s.text();log(`[portal-api-client] saveMyPrompt error: ${s.status} ${t}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] saveMyPrompt FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function deleteMyPrompt(e,o){try{if(log(`[portal-api-client] deleteMyPrompt called: id=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/cli/my-agents/${encodeURIComponent(e)}`,s=await fetch(r,{method:"DELETE",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){const t=await s.text();log(`[portal-api-client] deleteMyPrompt error: ${s.status}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] deleteMyPrompt FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function downloadMcpPackage(e,o){try{if(log(`[portal-api-client] downloadMcpPackage called: mcpId=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/download`;log(`[portal-api-client] Downloading MCP package: ${r}`);const s=await fetch(r,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){const t=await s.text();log(`[portal-api-client] downloadMcpPackage error: ${s.status} ${t}`);let e=`Download failed: HTTP ${s.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}const a=await s.json();return log(`[portal-api-client] downloadMcpPackage: ${a.data?.files?.length||0} files`),{success:!0,data:a.data}}catch(e){return log(`[portal-api-client] downloadMcpPackage FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function getGatewayConfig(t){try{if(!t)return{success:!1,error:"No token"};const e=`${PORTAL_URL}/mcp/user/me/gateway-config`;log(`[portal-api-client] getGatewayConfig: ${e}`);const o=await fetch(e,{method:"GET",headers:{"X-CLI-Token":t,"Content-Type":"application/json"},agent:e.startsWith("https")?httpsAgent:void 0});if(!o.ok)return{success:!1,error:`HTTP ${o.status}`};const r=await o.json();return log(`[portal-api-client] getGatewayConfig: ${r.data?.services?.length||0} services`),{success:!0,data:r.data}}catch(t){return log(`[portal-api-client] getGatewayConfig error: ${t.message}`),{success:!1,error:t.message}}}async function searchMcpServices(t,e){try{if(!e)return{success:!1,error:"No token"};const o=`${PORTAL_URL}/mcp/search?q=${encodeURIComponent(t)}`;log(`[portal-api-client] searchMcpServices: ${o}`);const r=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data}:{success:!1,error:`HTTP ${r.status}`}}catch(t){return log(`[portal-api-client] searchMcpServices error: ${t.message}`),{success:!1,error:t.message}}}async function installMcpService(t,e,o,r){try{if(!o)return{success:!1,error:"No token"};const s=`${PORTAL_URL}/mcp/user/me/install`,a={mcpId:t,alwaysOn:e??!1};r&&Object.keys(r).length>0&&(a.envOverrides=r);const n=await fetch(s,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(a)});return n.ok?{success:!0,data:(await n.json()).data}:{success:!1,error:`HTTP ${n.status}`}}catch(t){return log(`[portal-api-client] installMcpService error: ${t.message}`),{success:!1,error:t.message}}}async function setServiceDeactivated(t,e,o){try{if(!o)return{success:!1,error:"No token"};const r=e?"deactivate":"activate",s=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}/${r}`,a=await fetch(s,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:s.startsWith("https")?httpsAgent:void 0});return a.ok?{success:!0,data:(await a.json()).data}:{success:!1,error:`HTTP ${a.status}`}}catch(t){return log(`[portal-api-client] setServiceDeactivated error: ${t.message}`),{success:!1,error:t.message}}}async function uninstallMcpService(t,e){try{if(!e)return{success:!1,error:"No token"};const o=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}`,r=await fetch(o,{method:"DELETE",headers:{"X-CLI-Token":e,"Content-Type":"application/json"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data}:{success:!1,error:`HTTP ${r.status}`}}catch(t){return log(`[portal-api-client] uninstallMcpService error: ${t.message}`),{success:!1,error:t.message}}}async function updateMcpEnv(t,e,o){try{if(!o)return{success:!1,error:"No token"};const r=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}/env`,s=await fetch(r,{method:"PUT",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({env:e})});if(!s.ok)return{success:!1,error:`HTTP ${s.status}`};const a=await s.json();return{success:!0,data:a.data,warning:a.warning||null}}catch(t){return log(`[portal-api-client] updateMcpEnv error: ${t.message}`),{success:!1,error:t.message}}}async function callMcpToolProxy(t,e,o,r){try{if(!r)return{success:!1,error:"No token"};const s=`${PORTAL_URL}/mcp/${encodeURIComponent(t)}/call`,a=await fetch(s,{method:"POST",headers:{"X-CLI-Token":r,"Content-Type":"application/json"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({tool_name:e,arguments:o})});if(!a.ok){const t=await a.text();return{success:!1,error:`HTTP ${a.status}: ${t.slice(0,200)}`}}return await a.json()}catch(t){return log(`[portal-api-client] callMcpToolProxy error: ${t.message}`),{success:!1,error:t.message}}}fs.existsSync(LOG_DIR)||fs.mkdirSync(LOG_DIR,{recursive:!0}),module.exports={getPromptFromPortal:getPromptFromPortal,getSlotConfig:getSlotConfig,reportViolation:reportViolation,listAvailableMcps:listAvailableMcps,getMcpInfo:getMcpInfo,downloadMcpPackage:downloadMcpPackage,callMcpTool:callMcpTool,scanMcpCode:scanMcpCode,deployUserMcp:deployUserMcp,listUserCreatedMcps:listUserCreatedMcps,deleteUserMcp:deleteUserMcp,listMyPrompts:listMyPrompts,saveMyPrompt:saveMyPrompt,deleteMyPrompt:deleteMyPrompt,getGatewayConfig:getGatewayConfig,searchMcpServices:searchMcpServices,installMcpService:installMcpService,setServiceDeactivated:setServiceDeactivated,uninstallMcpService:uninstallMcpService,updateMcpEnv:updateMcpEnv,callMcpToolProxy:callMcpToolProxy};
|
|
1
|
+
const fetch=(...t)=>import("node-fetch").then(({default:e})=>e(...t)),https=require("https"),fs=require("fs"),path=require("path"),os=require("os"),{requireFromPackage:requireFromPackage}=require("../requireFromPackage.cjs"),{logger:logger}=requireFromPackage(require,"../../utils/logger"),{t:t}=requireFromPackage(require,"../../i18n"),PORTAL_URL_ENV=process.env.BLUELAMP_PORTAL_API_BASE_URL||process.env.PORTAL_URL||"",isDevEnvironment=PORTAL_URL_ENV.includes("localhost"),httpsAgent=new https.Agent({rejectUnauthorized:!isDevEnvironment}),PORTAL_URL=PORTAL_URL_ENV||(isDevEnvironment?"http://localhost:8430/api/bluelamp":"https://api.motohi.jp/api/bluelamp"),LOG_DIR=path.join(os.homedir(),".bluelamp-vscode","logs"),LOG_FILE=path.join(LOG_DIR,"mcp.log");function log(t){const e=`[${(new Date).toISOString()}] ${t}\n`;fs.appendFileSync(LOG_FILE,e),logger.debug(t)}let _cliVersion;function getCliVersion(){if(void 0!==_cliVersion)return _cliVersion;let t=__dirname;for(let e=0;e<6;e++){try{const e=require(path.join(t,"package.json"));if(e&&e.version&&e.bin&&Object.keys(e.bin).some(t=>/^[a-z]+\d+$/.test(t)))return _cliVersion=e.version,_cliVersion}catch(t){}const e=path.dirname(t);if(e===t)break;t=e}return _cliVersion=null,_cliVersion}async function getPromptFromPortal(e,o,r="ja"){try{if(log(`[portal-api-client] getPromptFromPortal called with keyword: ${e}, lang: ${r}, token: ${o?"present":"missing"}`),!o)return log("[portal-api-client] No authentication token provided"),{success:!1,error:t("mcp.noAuthToken")};const s=process.env.MUSUHI_TENANT_ID||"",a=encodeURIComponent(e);let n=`${PORTAL_URL}/cli/prompts/knowledge-injection/${a}?lang=${r}`;s&&(n+=`&tenant_id=${encodeURIComponent(s)}`),log(`[portal-api-client] Direct keyword search: ${n}`),log("[portal-api-client] About to call fetch...");const c=await fetch(n,{method:"GET",headers:{"X-CLI-Token":o,...getCliVersion()?{"X-CLI-Version":getCliVersion()}:{},...s?{"X-Tenant-ID":s}:{},"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:n.startsWith("https")?httpsAgent:void 0});if(log("[portal-api-client] Fetch completed"),log(`[portal-api-client] Response status: ${c.status}`),log(`[portal-api-client] Response headers: ${JSON.stringify(Object.fromEntries(c.headers.entries()))}`),!c.ok){log(`[portal-api-client] Portal API error: ${c.status}`);const e=await c.text();log(`[portal-api-client] Error response: ${e}`);let o=t("mcp.portalApiError",{status:c.status}),r=null;try{const t=JSON.parse(e);t.message&&(o=t.message),t.code&&(r=t.code)}catch(t){}return{success:!1,error:o,status:c.status,code:r}}const i=await c.json();if(log("[portal-api-client] Direct keyword search completed successfully"),!i.success)return log("[portal-api-client] API returned success: false"),{success:!1,error:i.message||t("mcp.apiReturnedError")};const l=i.data;if(!l)return log(`[portal-api-client] No prompt found for keyword: ${e}`),{success:!1,error:t("mcp.promptNotFoundForKeyword",{keyword:e})};if(log(`[portal-api-client] Found prompt: ${l.title}`),l.content||l.text)return{success:!0,knowledge:l.content||l.text,title:l.title,tags:l.tags||[]};try{const t=`${PORTAL_URL}/cli/prompts/${l.id}`;log(`[portal-api-client] Fetching prompt details from: ${t}`);const e=await fetch(t,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:httpsAgent});if(e.ok){const t=await e.json();if(t.success&&t.data?.prompt?.content)return log("[portal-api-client] Successfully fetched prompt content"),{success:!0,knowledge:t.data.prompt.content,title:l.title||l.name,tags:l.tags}}}catch(t){log(`[portal-api-client] Error fetching prompt details: ${t.message}`)}return{success:!0,knowledge:l.description||`# ${l.title}\n\n${t("mcp.knowledgeLoadError",{error:"detail fetch failed"})}`,title:l.title||l.name,tags:l.tags}}catch(e){return log(`[portal-api-client] FATAL ERROR in getPromptFromPortal: ${e.message}`),log(`[portal-api-client] Error stack: ${e.stack}`),logger.error(`Failed to get prompt from Portal: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message}),networkFailure:Boolean(e&&"FetchError"===e.name)}}}async function getSlotConfig(t,e){try{if(!e)return log("[portal-api-client] getSlotConfig: No token"),{ok:!1,reason:"no-token"};const o=process.env.MUSUHI_TENANT_ID||"",r=`${PORTAL_URL}/cli/slot-config/${t}`;log(`[portal-api-client] Fetching slot config: ${r}${o?` (tenant=${o})`:""}`);const s=await fetch(r,{method:"GET",headers:{"X-CLI-Token":e,...getCliVersion()?{"X-CLI-Version":getCliVersion()}:{},...o?{"X-Tenant-ID":o}:{},"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){log(`[portal-api-client] Slot config API error: ${s.status}`);let t=null;try{const e=await s.json();t=e?.error?.message||e?.message||null}catch{t=null}return 404===s.status?{ok:!1,reason:"not-configured",status:404,message:t}:403===s.status?{ok:!1,reason:"rank",status:403,message:t}:{ok:!1,reason:"http",status:s.status,message:t}}const a=await s.json();return a.success&&a.data?.keyword?(log(`[portal-api-client] Slot ${t}: custom keyword "${a.data.keyword}"`),{ok:!0,keyword:a.data.keyword}):(log(`[portal-api-client] Slot ${t}: no custom config`),{ok:!0,keyword:null})}catch(t){return log(`[portal-api-client] getSlotConfig error: ${t.message}`),{ok:!1,reason:"network",status:null,message:t.message}}}async function reportViolation(e,o,r,s){try{if(log(`[portal-api-client] reportViolation called: severity=${o}`),!r)return{success:!1,error:t("mcp.violationNoToken")};const a=`${PORTAL_URL}/cli/report-violation`,n={reason:e,severity:o};s&&(n.userMessage=s);const c=await fetch(a,{method:"POST",headers:{"X-CLI-Token":r,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:a.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(n)});if(!c.ok){const e=await c.text();return log(`[portal-api-client] reportViolation error: ${c.status} ${e}`),{success:!1,error:t("mcp.violationApiError",{status:c.status})}}const i=await c.json();return log(`[portal-api-client] reportViolation result: action=${i.action}, count=${i.violationCount}`),i}catch(e){return log(`[portal-api-client] reportViolation FATAL: ${e.message}`),{success:!1,error:t("mcp.violationCommunicationError",{error:e.message})}}}async function listAvailableMcps(e={},o,r="ja"){try{if(log(`[portal-api-client] listAvailableMcps called: ${JSON.stringify(e)}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const s=new URLSearchParams;e.search&&s.set("search",e.search),e.limit&&s.set("limit",String(e.limit)),s.set("lang",r);const a=`${PORTAL_URL}/mcp/catalog?${s.toString()}`;log(`[portal-api-client] Fetching MCP catalog: ${a}`);const n=await fetch(a,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:a.startsWith("https")?httpsAgent:void 0});if(!n.ok){const e=await n.text();log(`[portal-api-client] listAvailableMcps error: ${n.status} ${e}`);let o=t("mcp.listMcpsFailed",{error:`HTTP ${n.status}`});try{const t=JSON.parse(e);t.message&&(o=t.message)}catch(t){}return{success:!1,error:o}}const c=await n.json();return log(`[portal-api-client] listAvailableMcps: ${c.data?.length||0} MCPs found`),{success:!0,data:c.data||[]}}catch(e){return log(`[portal-api-client] listAvailableMcps FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function getMcpInfo(e,o,r="ja"){try{if(log(`[portal-api-client] getMcpInfo called: mcpId=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const s=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/details?lang=${r}`;log(`[portal-api-client] Fetching MCP info: ${s}`);const a=await fetch(s,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0});if(!a.ok){const e=await a.text();log(`[portal-api-client] getMcpInfo error: ${a.status} ${e}`);let o=t("mcp.getMcpInfoFailed",{error:`HTTP ${a.status}`});try{const t=JSON.parse(e);t.message&&(o=t.message)}catch(t){}return{success:!1,error:o}}const n=await a.json();return log(`[portal-api-client] getMcpInfo: found ${n.data?.name||e}`),{success:!0,data:n.data}}catch(e){return log(`[portal-api-client] getMcpInfo FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function callMcpTool(e,o,r,s){try{if(log(`[portal-api-client] callMcpTool called: mcpId=${e}, tool=${o}`),!s)return{success:!1,error:t("mcp.noAuthToken")};const a=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/call`,n={tool_name:o,arguments:r};log(`[portal-api-client] Calling MCP tool: ${a}`);const c=await fetch(a,{method:"POST",headers:{"X-CLI-Token":s,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:a.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(n)});if(!c.ok){const e=await c.text();log(`[portal-api-client] callMcpTool error: ${c.status} ${e}`);let o=t("mcp.callToolFailed",{error:`HTTP ${c.status}`});try{const t=JSON.parse(e);t.message&&(o=t.message)}catch(t){}return{success:!1,error:o}}const i=await c.json();return log("[portal-api-client] callMcpTool: success"),{success:!0,data:i.data}}catch(e){return log(`[portal-api-client] callMcpTool FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function scanMcpCode(e,o){try{if(log("[portal-api-client] scanMcpCode called"),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/scan`,s=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({code:e})});if(!s.ok){const t=await s.text();return log(`[portal-api-client] scanMcpCode error: ${s.status} ${t}`),{success:!1,error:`HTTP ${s.status}`}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] scanMcpCode FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function deployUserMcp(e,o){try{if(log(`[portal-api-client] deployUserMcp called: name=${e.name}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/deploy`,s=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(e)});if(!s.ok){const t=await s.text();log(`[portal-api-client] deployUserMcp error: ${s.status} ${t}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);if(o.error&&(e=o.error),o.details)return{success:!1,error:e,details:o.details}}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] deployUserMcp FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function listUserCreatedMcps(e){try{if(log("[portal-api-client] listUserCreatedMcps called"),!e)return{success:!1,error:t("mcp.noAuthToken")};const o=`${PORTAL_URL}/mcp/user/me/created`,r=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data}:(await r.text(),log(`[portal-api-client] listUserCreatedMcps error: ${r.status}`),{success:!1,error:`HTTP ${r.status}`})}catch(e){return log(`[portal-api-client] listUserCreatedMcps FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function deleteUserMcp(e,o){try{if(log(`[portal-api-client] deleteUserMcp called: mcpId=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}`,s=await fetch(r,{method:"DELETE",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){const t=await s.text();log(`[portal-api-client] deleteUserMcp error: ${s.status}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);o.error&&(e=o.error)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] deleteUserMcp FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function listMyPrompts(e){try{if(log("[portal-api-client] listMyPrompts called"),!e)return{success:!1,error:t("mcp.noAuthToken")};const o=`${PORTAL_URL}/cli/my-agents`,r=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data||[]}:(await r.text(),log(`[portal-api-client] listMyPrompts error: ${r.status}`),{success:!1,error:`HTTP ${r.status}`})}catch(e){return log(`[portal-api-client] listMyPrompts FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function saveMyPrompt(e,o){try{if(log(`[portal-api-client] saveMyPrompt called: title=${e.title}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/cli/my-agents`,s=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(e)});if(!s.ok){const t=await s.text();log(`[portal-api-client] saveMyPrompt error: ${s.status} ${t}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] saveMyPrompt FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function deleteMyPrompt(e,o){try{if(log(`[portal-api-client] deleteMyPrompt called: id=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/cli/my-agents/${encodeURIComponent(e)}`,s=await fetch(r,{method:"DELETE",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){const t=await s.text();log(`[portal-api-client] deleteMyPrompt error: ${s.status}`);let e=`HTTP ${s.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await s.json()).data}}catch(e){return log(`[portal-api-client] deleteMyPrompt FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function downloadMcpPackage(e,o){try{if(log(`[portal-api-client] downloadMcpPackage called: mcpId=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/download`;log(`[portal-api-client] Downloading MCP package: ${r}`);const s=await fetch(r,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BlueLamp-VSCode-MCP/1.0"},agent:r.startsWith("https")?httpsAgent:void 0});if(!s.ok){const t=await s.text();log(`[portal-api-client] downloadMcpPackage error: ${s.status} ${t}`);let e=`Download failed: HTTP ${s.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}const a=await s.json();return log(`[portal-api-client] downloadMcpPackage: ${a.data?.files?.length||0} files`),{success:!0,data:a.data}}catch(e){return log(`[portal-api-client] downloadMcpPackage FATAL: ${e.message}`),{success:!1,error:t("mcp.portalCommunicationError",{error:e.message})}}}async function getGatewayConfig(t){try{if(!t)return{success:!1,error:"No token"};const e=`${PORTAL_URL}/mcp/user/me/gateway-config`;log(`[portal-api-client] getGatewayConfig: ${e}`);const o=await fetch(e,{method:"GET",headers:{"X-CLI-Token":t,"Content-Type":"application/json"},agent:e.startsWith("https")?httpsAgent:void 0});if(!o.ok)return{success:!1,error:`HTTP ${o.status}`};const r=await o.json();return log(`[portal-api-client] getGatewayConfig: ${r.data?.services?.length||0} services`),{success:!0,data:r.data}}catch(t){return log(`[portal-api-client] getGatewayConfig error: ${t.message}`),{success:!1,error:t.message}}}async function searchMcpServices(t,e){try{if(!e)return{success:!1,error:"No token"};const o=`${PORTAL_URL}/mcp/search?q=${encodeURIComponent(t)}`;log(`[portal-api-client] searchMcpServices: ${o}`);const r=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data}:{success:!1,error:`HTTP ${r.status}`}}catch(t){return log(`[portal-api-client] searchMcpServices error: ${t.message}`),{success:!1,error:t.message}}}async function installMcpService(t,e,o,r){try{if(!o)return{success:!1,error:"No token"};const s=`${PORTAL_URL}/mcp/user/me/install`,a={mcpId:t,alwaysOn:e??!1};r&&Object.keys(r).length>0&&(a.envOverrides=r);const n=await fetch(s,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(a)});return n.ok?{success:!0,data:(await n.json()).data}:{success:!1,error:`HTTP ${n.status}`}}catch(t){return log(`[portal-api-client] installMcpService error: ${t.message}`),{success:!1,error:t.message}}}async function setServiceDeactivated(t,e,o){try{if(!o)return{success:!1,error:"No token"};const r=e?"deactivate":"activate",s=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}/${r}`,a=await fetch(s,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:s.startsWith("https")?httpsAgent:void 0});return a.ok?{success:!0,data:(await a.json()).data}:{success:!1,error:`HTTP ${a.status}`}}catch(t){return log(`[portal-api-client] setServiceDeactivated error: ${t.message}`),{success:!1,error:t.message}}}async function uninstallMcpService(t,e){try{if(!e)return{success:!1,error:"No token"};const o=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}`,r=await fetch(o,{method:"DELETE",headers:{"X-CLI-Token":e,"Content-Type":"application/json"},agent:o.startsWith("https")?httpsAgent:void 0});return r.ok?{success:!0,data:(await r.json()).data}:{success:!1,error:`HTTP ${r.status}`}}catch(t){return log(`[portal-api-client] uninstallMcpService error: ${t.message}`),{success:!1,error:t.message}}}async function updateMcpEnv(t,e,o){try{if(!o)return{success:!1,error:"No token"};const r=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}/env`,s=await fetch(r,{method:"PUT",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:r.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({env:e})});if(!s.ok)return{success:!1,error:`HTTP ${s.status}`};const a=await s.json();return{success:!0,data:a.data,warning:a.warning||null}}catch(t){return log(`[portal-api-client] updateMcpEnv error: ${t.message}`),{success:!1,error:t.message}}}async function callMcpToolProxy(t,e,o,r){try{if(!r)return{success:!1,error:"No token"};const s=`${PORTAL_URL}/mcp/${encodeURIComponent(t)}/call`,a=await fetch(s,{method:"POST",headers:{"X-CLI-Token":r,"Content-Type":"application/json"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({tool_name:e,arguments:o})});if(!a.ok){const t=await a.text();return{success:!1,error:`HTTP ${a.status}: ${t.slice(0,200)}`}}return await a.json()}catch(t){return log(`[portal-api-client] callMcpToolProxy error: ${t.message}`),{success:!1,error:t.message}}}fs.existsSync(LOG_DIR)||fs.mkdirSync(LOG_DIR,{recursive:!0}),module.exports={getPromptFromPortal:getPromptFromPortal,getSlotConfig:getSlotConfig,reportViolation:reportViolation,listAvailableMcps:listAvailableMcps,getMcpInfo:getMcpInfo,downloadMcpPackage:downloadMcpPackage,callMcpTool:callMcpTool,scanMcpCode:scanMcpCode,deployUserMcp:deployUserMcp,listUserCreatedMcps:listUserCreatedMcps,deleteUserMcp:deleteUserMcp,listMyPrompts:listMyPrompts,saveMyPrompt:saveMyPrompt,deleteMyPrompt:deleteMyPrompt,getGatewayConfig:getGatewayConfig,searchMcpServices:searchMcpServices,installMcpService:installMcpService,setServiceDeactivated:setServiceDeactivated,uninstallMcpService:uninstallMcpService,updateMcpEnv:updateMcpEnv,callMcpToolProxy:callMcpToolProxy};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** 起動子 → gateway へ一時ファイルのパスを渡す env 名。 */
|
|
2
|
+
export declare const SYS_PROMPT_ENV = "BLUELAMP_SYS_PROMPT_FILE";
|
|
3
|
+
/** gateway が起きなかったときに起動子が消すまでの上限(ms)。 */
|
|
4
|
+
export declare const SYS_PROMPT_SAFETY_CAP_MS: number;
|
|
5
|
+
/** 起動子が作る一時ファイル名(`randomHex` は 8 byte の hex=16 文字)。 */
|
|
6
|
+
export declare function sysPromptFileName(randomHex: string): string;
|
|
7
|
+
/** `candidate` が起動子の作る形で `tmpdir` の直下にあるか(消してよいか)。 */
|
|
8
|
+
export declare function isOwnSysPromptPath(candidate: string | undefined | null, tmpdir: string): boolean;
|
|
9
|
+
//# sourceMappingURL=sysPromptHandoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sysPromptHandoff.d.ts","sourceRoot":"","sources":["../../src/lib/sysPromptHandoff.ts"],"names":[],"mappings":"AAmBA,yCAAyC;AACzC,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD,yCAAyC;AACzC,eAAO,MAAM,wBAAwB,QAAiB,CAAC;AAIvD,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAIhG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(t,e,r,n){void 0===n&&(n=r);var o=Object.getOwnPropertyDescriptor(e,r);o&&!("get"in o?!e.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,n,o)}:function(t,e,r,n){void 0===n&&(n=r),t[n]=e[r]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),__importStar=this&&this.__importStar||function(){var t=function(e){return t=Object.getOwnPropertyNames||function(t){var e=[];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[e.length]=r);return e},t(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(null!=e)for(var n=t(e),o=0;o<n.length;o++)"default"!==n[o]&&__createBinding(r,e,n[o]);return __setModuleDefault(r,e),r}}();Object.defineProperty(exports,"__esModule",{value:!0}),exports.SYS_PROMPT_SAFETY_CAP_MS=exports.SYS_PROMPT_ENV=void 0,exports.sysPromptFileName=sysPromptFileName,exports.isOwnSysPromptPath=isOwnSysPromptPath;const path=__importStar(require("node:path"));exports.SYS_PROMPT_ENV="BLUELAMP_SYS_PROMPT_FILE",exports.SYS_PROMPT_SAFETY_CAP_MS=6e5;const SYS_PROMPT_NAME=/^bl-sys-[0-9a-f]{16}\.txt$/;function sysPromptFileName(t){return`bl-sys-${t}.txt`}function isOwnSysPromptPath(t,e){if(!t||!path.isAbsolute(t))return!1;const r=path.resolve(t);return path.dirname(r)===path.resolve(e)&&SYS_PROMPT_NAME.test(path.basename(r))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sysPromptHandoff.js","sourceRoot":"","sources":["../../src/lib/sysPromptHandoff.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,8CAEC;AAGD,gDAIC;AArCD;;;;;;;;;;;;;;;;GAgBG;AACH,gDAAkC;AAElC,yCAAyC;AAC5B,QAAA,cAAc,GAAG,0BAA0B,CAAC;AAEzD,yCAAyC;AAC5B,QAAA,wBAAwB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD,MAAM,eAAe,GAAG,4BAA4B,CAAC;AAErD,uDAAuD;AACvD,SAAgB,iBAAiB,CAAC,SAAiB;IACjD,OAAO,UAAU,SAAS,MAAM,CAAC;AACnC,CAAC;AAED,sDAAsD;AACtD,SAAgB,kBAAkB,CAAC,SAAoC,EAAE,MAAc;IACrF,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1G,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bluelamp",
|
|
3
|
-
"version": "6.6.
|
|
3
|
+
"version": "6.6.12",
|
|
4
4
|
"description": "BlueLamp CLI: 旧 bluelampportal/mcp の auth / 200+ bluelamp1-N binary / postinstall scripts を TypeScript 化して monorepo に統合したパッケージ。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bluelamp",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"preferGlobal": true,
|
|
25
|
+
"bluelampLauncherFeatures": [
|
|
26
|
+
"task-file"
|
|
27
|
+
],
|
|
25
28
|
"repository": {
|
|
26
29
|
"type": "git",
|
|
27
30
|
"url": "git+https://github.com/mikoto-co-jp/mikoto-monorepo.git",
|