bizboost 6.4.13 → 6.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -11
- package/dist/auth/InteractiveLogin.js +1 -1
- package/dist/auth/PortalAuthClient.d.ts +1 -1
- package/dist/auth/PortalAuthClient.js +1 -1
- package/dist/auth/TokenManager.d.ts +5 -14
- package/dist/auth/TokenManager.d.ts.map +1 -1
- package/dist/auth/TokenManager.js +1 -1
- package/dist/auth/TokenManager.js.map +1 -1
- package/dist/auth/portalTokenStore.d.ts +19 -0
- package/dist/auth/portalTokenStore.d.ts.map +1 -0
- package/dist/auth/portalTokenStore.js +1 -0
- package/dist/auth/portalTokenStore.js.map +1 -0
- package/dist/bin/bizboost201.mjs +2 -0
- package/dist/bin/bizboost202.mjs +2 -0
- package/dist/bin/bizboost203.mjs +2 -0
- package/dist/bin/bizboost204.mjs +2 -0
- package/dist/bin/bizboost205.mjs +2 -0
- package/dist/bin/bizboost206.mjs +2 -0
- package/dist/bin/bizboost207.mjs +2 -0
- package/dist/bin/bizboost208.mjs +2 -0
- package/dist/bin/bizboost209.mjs +2 -0
- package/dist/bin/bizboost210.mjs +2 -0
- package/dist/bin/bizboost211.mjs +2 -0
- package/dist/bin/bizboost212.mjs +2 -0
- package/dist/bin/bizboost213.mjs +2 -0
- package/dist/bin/bizboost214.mjs +2 -0
- package/dist/bin/bizboost215.mjs +2 -0
- package/dist/bin/bizboost216.mjs +2 -0
- package/dist/bin/bizboost217.mjs +2 -0
- package/dist/bin/bizboost218.mjs +2 -0
- package/dist/bin/bizboost219.mjs +2 -0
- package/dist/bin/bizboost220.mjs +2 -0
- package/dist/bin/bizboost221.mjs +2 -0
- package/dist/bin/bizboost222.mjs +2 -0
- package/dist/bin/bizboost223.mjs +2 -0
- package/dist/bin/bizboost224.mjs +2 -0
- package/dist/bin/bizboost225.mjs +2 -0
- package/dist/bin/bizboost226.mjs +2 -0
- package/dist/bin/bizboost227.mjs +2 -0
- package/dist/bin/bizboost228.mjs +2 -0
- package/dist/bin/bizboost229.mjs +2 -0
- package/dist/bin/bizboost230.mjs +2 -0
- package/dist/bin/bizboost231.mjs +2 -0
- package/dist/bin/bizboost232.mjs +2 -0
- package/dist/bin/bizboost233.mjs +2 -0
- package/dist/bin/bizboost234.mjs +2 -0
- package/dist/bin/bizboost235.mjs +2 -0
- package/dist/bin/bizboost236.mjs +2 -0
- package/dist/bin/bizboost237.mjs +2 -0
- package/dist/bin/bizboost238.mjs +2 -0
- package/dist/bin/bizboost239.mjs +2 -0
- package/dist/bin/bizboost240.mjs +2 -0
- package/dist/bin/bizboost241.mjs +2 -0
- package/dist/bin/bizboost242.mjs +2 -0
- package/dist/bin/bizboost243.mjs +2 -0
- package/dist/bin/bizboost244.mjs +2 -0
- package/dist/bin/bizboost245.mjs +2 -0
- package/dist/bin/bizboost246.mjs +2 -0
- package/dist/bin/bizboost247.mjs +2 -0
- package/dist/bin/bizboost248.mjs +2 -0
- package/dist/bin/bizboost249.mjs +2 -0
- package/dist/bin/bizboost250.mjs +2 -0
- package/dist/bin/bizboost251.mjs +2 -0
- package/dist/bin/bizboost252.mjs +2 -0
- package/dist/bin/bizboost253.mjs +2 -0
- package/dist/bin/bizboost254.mjs +2 -0
- package/dist/bin/bizboost255.mjs +2 -0
- package/dist/bin/bizboost256.mjs +2 -0
- package/dist/bin/bizboost257.mjs +2 -0
- package/dist/bin/bizboost258.mjs +2 -0
- package/dist/bin/bizboost259.mjs +2 -0
- package/dist/bin/bizboost260.mjs +2 -0
- package/dist/bin/bizboost261.mjs +2 -0
- package/dist/bin/bizboost262.mjs +2 -0
- package/dist/bin/bizboost263.mjs +2 -0
- package/dist/bin/bizboost264.mjs +2 -0
- package/dist/bin/bizboost265.mjs +2 -0
- package/dist/bin/bizboost266.mjs +2 -0
- package/dist/bin/bizboost267.mjs +2 -0
- package/dist/bin/bizboost268.mjs +2 -0
- package/dist/bin/bizboost269.mjs +2 -0
- package/dist/bin/bizboost270.mjs +2 -0
- package/dist/bin/bizboost271.mjs +2 -0
- package/dist/bin/bizboost272.mjs +2 -0
- package/dist/bin/bizboost273.mjs +2 -0
- package/dist/bin/bizboost274.mjs +2 -0
- package/dist/bin/bizboost275.mjs +2 -0
- package/dist/bin/bizboost276.mjs +2 -0
- package/dist/bin/bizboost277.mjs +2 -0
- package/dist/bin/bizboost278.mjs +2 -0
- package/dist/bin/bizboost279.mjs +2 -0
- package/dist/bin/bizboost280.mjs +2 -0
- package/dist/bin/bizboost281.mjs +2 -0
- package/dist/bin/bizboost282.mjs +2 -0
- package/dist/bin/bizboost283.mjs +2 -0
- package/dist/bin/bizboost284.mjs +2 -0
- package/dist/bin/bizboost285.mjs +2 -0
- package/dist/bin/bizboost286.mjs +2 -0
- package/dist/bin/bizboost287.mjs +2 -0
- package/dist/bin/bizboost288.mjs +2 -0
- package/dist/bin/bizboost289.mjs +2 -0
- package/dist/bin/bizboost290.mjs +2 -0
- package/dist/bin/bizboost291.mjs +2 -0
- package/dist/bin/bizboost292.mjs +2 -0
- package/dist/bin/bizboost293.mjs +2 -0
- package/dist/bin/bizboost294.mjs +2 -0
- package/dist/bin/bizboost295.mjs +2 -0
- package/dist/bin/bizboost296.mjs +2 -0
- package/dist/bin/bizboost297.mjs +2 -0
- package/dist/bin/bizboost298.mjs +2 -0
- package/dist/bin/bizboost299.mjs +2 -0
- package/dist/bin/bizboost300.mjs +2 -0
- package/dist/bin/bluelamp-vscode-base.mjs +1 -1
- package/dist/i18n/en.d.ts +6 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +1 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/ja.d.ts +6 -1
- package/dist/i18n/ja.d.ts.map +1 -1
- package/dist/i18n/ja.js +1 -1
- package/dist/i18n/ja.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/account-mcp/index.cjs +1 -1
- package/dist/lib/accountAssurance.d.ts +41 -0
- package/dist/lib/accountAssurance.d.ts.map +1 -0
- package/dist/lib/accountAssurance.js +1 -0
- package/dist/lib/accountAssurance.js.map +1 -0
- package/dist/lib/claudeAccount.d.ts +45 -0
- package/dist/lib/claudeAccount.d.ts.map +1 -0
- package/dist/lib/claudeAccount.js +1 -0
- package/dist/lib/claudeAccount.js.map +1 -0
- package/dist/lib/claudeOnboardingSeed.d.ts +131 -0
- package/dist/lib/claudeOnboardingSeed.d.ts.map +1 -0
- package/dist/lib/claudeOnboardingSeed.js +1 -0
- package/dist/lib/claudeOnboardingSeed.js.map +1 -0
- package/dist/lib/debugRedaction.d.ts +42 -0
- package/dist/lib/debugRedaction.d.ts.map +1 -0
- package/dist/lib/debugRedaction.js +1 -0
- package/dist/lib/debugRedaction.js.map +1 -0
- package/dist/lib/headlessLaunch.d.ts +63 -0
- package/dist/lib/headlessLaunch.d.ts.map +1 -0
- package/dist/lib/headlessLaunch.js +1 -0
- package/dist/lib/headlessLaunch.js.map +1 -0
- package/dist/lib/inheritedTenant.d.ts +44 -0
- package/dist/lib/inheritedTenant.d.ts.map +1 -0
- package/dist/lib/inheritedTenant.js +1 -0
- package/dist/lib/inheritedTenant.js.map +1 -0
- package/dist/lib/mcp-server/index.cjs +1 -1
- package/dist/lib/mcp-server/install-package.cjs +1 -0
- package/dist/lib/mcp-server/mcp-version-advisor.cjs +1 -0
- package/dist/lib/mcp-server/portal-api-client.cjs +1 -1
- package/dist/lib/mcpJsonSelfHeal.d.ts +77 -0
- package/dist/lib/mcpJsonSelfHeal.d.ts.map +1 -0
- package/dist/lib/mcpJsonSelfHeal.js +1 -0
- package/dist/lib/mcpJsonSelfHeal.js.map +1 -0
- package/dist/lib/promptFailureGuidance.d.ts +30 -0
- package/dist/lib/promptFailureGuidance.d.ts.map +1 -0
- package/dist/lib/promptFailureGuidance.js +1 -0
- package/dist/lib/promptFailureGuidance.js.map +1 -0
- package/dist/lib/requireFromPackage.cjs +1 -0
- package/dist/lib/slotKeywordResolve.d.ts +69 -0
- package/dist/lib/slotKeywordResolve.d.ts.map +1 -0
- package/dist/lib/slotKeywordResolve.js +1 -0
- package/dist/lib/slotKeywordResolve.js.map +1 -0
- package/dist/lib/tempMcpConfig.d.ts +79 -0
- package/dist/lib/tempMcpConfig.d.ts.map +1 -0
- package/dist/lib/tempMcpConfig.js +1 -0
- package/dist/lib/tempMcpConfig.js.map +1 -0
- package/dist/lib/whoami.d.ts +34 -0
- package/dist/lib/whoami.d.ts.map +1 -0
- package/dist/lib/whoami.js +1 -0
- package/dist/lib/whoami.js.map +1 -0
- package/dist/scripts/generate-commands.js +1 -1
- package/dist/shared/claude-bin.js +1 -0
- package/dist/utils/logger.d.ts +1 -1
- package/package.json +114 -11
- package/verify-release.sh +154 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debugRedaction.js","sourceRoot":"","sources":["../../src/lib/debugRedaction.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAoBH,0DA6BC;AASD,sDAGC;AA3DD,mCAAmC;AACtB,QAAA,QAAQ,GAAG,KAAK,CAAC;AAE9B,+CAA+C;AAClC,QAAA,kBAAkB,GAAG,4CAA4C,CAAC;AAO/E;;;;;;GAMG;AACH,SAAgB,uBAAuB,CAAC,UAAkB;IACxD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,0BAAkB,CAAC;IAC5B,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,0BAAkB,CAAC;IAErE,MAAM,IAAI,GAAG,MAAyD,CAAC;IACvE,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC;IAChC,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,SAAS;YAClD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;YACtB,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACnC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACnC,GAAG,CAAC,GAAG,CAAC,GAAG,gBAAQ,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;QAChC,OAAO,0BAAkB,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,qBAAqB,CAAC,KAAc;IAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACpE,OAAO,gBAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 板 #458: ヘッドレス(`-p` / `--print`)起動で、利用者が渡した作業指示を claude まで届ける。
|
|
3
|
+
*
|
|
4
|
+
* 背景(2026-08-26 実測・stub claude で claude が受け取る argv を録って確認):
|
|
5
|
+
* `bluelampNNN -p "<指示>" --output-format text </dev/null` で起動したとき、
|
|
6
|
+
* claude が実際に受け取っていた argv は **これだけ**だった。
|
|
7
|
+
*
|
|
8
|
+
* [ "<バナー+『ユーザーへの挨拶から自然に開始してください』>",
|
|
9
|
+
* "--dangerously-skip-permissions", "--mcp-config", "<temp>" ]
|
|
10
|
+
*
|
|
11
|
+
* 🚨 **`-p` も `--output-format text` も 1 つも入っていない**。
|
|
12
|
+
* launcher は `process.argv.slice(2)` を **どこにも渡していなかった**(`--debug` の有無だけ見る)。
|
|
13
|
+
*
|
|
14
|
+
* ⇒ 起票時の推定「挨拶指示が -p と競合/上書きしている」は**症状としては合っているが原因は別**である。
|
|
15
|
+
* 実体は **①利用者の引数が 1 つも転送されていない ②挨拶用の定型が唯一の prompt として渡る**の 2 つ。
|
|
16
|
+
* ②だけ直しても(挨拶文を消しても)①が残る限り指示は届かない。**両方直して初めて閉じる。**
|
|
17
|
+
* (claude は `-p` を受け取っていないので対話モードで起動し、`</dev/null` で stdin が閉じるため
|
|
18
|
+
* 挨拶だけ返して exit 0 になる=呼び出し側からは「成功」に見える=無言スキップ。)
|
|
19
|
+
*
|
|
20
|
+
* 設計:
|
|
21
|
+
* - **ヘッドレスでは挨拶用の initialPrompt を注入しない**(利用者の `-p` を唯一の prompt にする)
|
|
22
|
+
* - **どちらのモードでも利用者の引数を転送する**(ただし launcher 自身が消費する flag は除く)
|
|
23
|
+
* - 🚨 転送から除くのは `--mcp-config` と `--debug` の 2 つだけ。
|
|
24
|
+
* `--mcp-config` は orchestrator がテナント指定を載せて渡してくるもので(板 #432)、
|
|
25
|
+
* launcher が読んだうえで**自前の一時 config を組み直して**渡している。素通ししてしまうと
|
|
26
|
+
* claude 側で bluelamp サーバー定義が二重になり、#432 で直したテナント引き継ぎを壊す。
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* 利用者がヘッドレス起動を要求しているか。
|
|
30
|
+
* ⚠️ 判定は `-p` / `--print` の**現れ方**だけで行い、値は見ない
|
|
31
|
+
* (`-p ""` も「ヘッドレスで動かせ」という明示の指示である=ticket の実測表 3 行目)。
|
|
32
|
+
*/
|
|
33
|
+
export declare function isHeadlessArgv(argv: readonly string[]): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* launcher が消費する flag を取り除く。値つき flag は `--flag value` / `--flag=value` の両形に対応。
|
|
36
|
+
* それ以外は**順序を保ったまま素通し**する(claude の引数解釈に手を入れない)。
|
|
37
|
+
*/
|
|
38
|
+
export declare function stripLauncherOwnedFlags(argv: readonly string[]): string[];
|
|
39
|
+
export interface BuildClaudeArgsInput {
|
|
40
|
+
/** 対話起動でだけ渡す、バナー+「挨拶から開始してください」の定型。 */
|
|
41
|
+
initialPrompt: string;
|
|
42
|
+
/** `--append-system-prompt-file <path>` 等(本体プロンプト注入・両モード共通)。 */
|
|
43
|
+
sysArgs: readonly string[];
|
|
44
|
+
/** launcher が組み直した一時 MCP config の path。 */
|
|
45
|
+
configPath: string;
|
|
46
|
+
/** `process.argv.slice(2)`。 */
|
|
47
|
+
userArgs: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
export interface BuildClaudeArgsResult {
|
|
50
|
+
/** claude に渡す引数(先頭の positional prompt を含む)。 */
|
|
51
|
+
args: string[];
|
|
52
|
+
/** ヘッドレスと判定したか。launcher 自身の標準出力の出し分けに使う。 */
|
|
53
|
+
headless: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* claude へ渡す引数を組む。**モードの違いはここ 1 箇所に閉じる。**
|
|
57
|
+
*
|
|
58
|
+
* 対話: `[挨拶定型, --dangerously-skip-permissions, ...sysArgs, --mcp-config, <path>, ...利用者引数]`
|
|
59
|
+
* ヘッドレス: `[--dangerously-skip-permissions, ...sysArgs, --mcp-config, <path>, ...利用者引数]`
|
|
60
|
+
* ⇒ 挨拶定型が無いので、利用者の `-p "<指示>"` が唯一の prompt になる。
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildClaudeArgs(input: BuildClaudeArgsInput): BuildClaudeArgsResult;
|
|
63
|
+
//# sourceMappingURL=headlessLaunch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headlessLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/headlessLaunch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AASH;;;;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"}
|
|
@@ -0,0 +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(s){return s.some(s=>"string"==typeof s&&HEADLESS_FLAGS.includes(s))}function stripLauncherOwnedFlags(s){const e=[];for(let r=0;r<s.length;r+=1){const t=s[r];if("string"!=typeof t)continue;if(LAUNCHER_OWNED_BOOLEAN_FLAGS.includes(t))continue;const n=LAUNCHER_OWNED_VALUE_FLAGS.find(s=>t===s||t.startsWith(`${s}=`));if(n){if(t===n){const e=s[r+1];"string"!=typeof e||e.startsWith("-")||(r+=1)}}else e.push(t)}return e}function buildClaudeArgs(s){const e=isHeadlessArgv(s.userArgs),r=stripLauncherOwnedFlags(s.userArgs);return{args:[...e?[]:[s.initialPrompt],"--dangerously-skip-permissions",...s.sysArgs,"--mcp-config",s.configPath,...r],headless:e}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headlessLaunch.js","sourceRoot":"","sources":["../../src/lib/headlessLaunch.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;AAcH,wCAEC;AAMD,0DAwBC;AA2BD,0CAcC;AArFD,+CAA+C;AAC/C,MAAM,0BAA0B,GAAG,CAAC,cAAc,CAAU,CAAC;AAC7D,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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* launcher が **受け取った** `--mcp-config` から、テナント指定を引き継ぐ。
|
|
3
|
+
*
|
|
4
|
+
* 背景(板 #432・2026-08-23 実測):
|
|
5
|
+
* orchestrator は `spawn_monster(bl_tenant_id)` を受けると
|
|
6
|
+
* `~/.claude/monsters/<id>/.mcp.json` に `mcpServers.bluelamp.env.MUSUHI_TENANT_ID` を書き、
|
|
7
|
+
* `bluelampNN --mcp-config <そのファイル>` で launcher を起動する。
|
|
8
|
+
* ところが launcher(`bin/bluelamp-vscode-base.mjs`)は **受け取った config を 1 バイトも読まず**、
|
|
9
|
+
* `createMCPConfig()` で一時 config を**ゼロから組み直す**。値の出どころは自分の `process.env` だけで、
|
|
10
|
+
* その `process.env.MUSUHI_TENANT_ID` は「アカウントの `active_tenant_id`」からしか入らない。
|
|
11
|
+
* ⇒ **`bl_tenant_id` は「渡した config の中」にしか無いので、構造上ぜったいに拾われなかった。**
|
|
12
|
+
*
|
|
13
|
+
* 実測(走行中プロセス 2 ペア):
|
|
14
|
+
* orchestrator が書いた値 = `7552b5a4…` / 子が実際に使う値 = **`''`(空文字)**
|
|
15
|
+
* 🚨 **キーは在って値だけが空**だったので、「キーが在るか」で見る検査は通ってしまう。
|
|
16
|
+
* ⇒ 本モジュールの受け入れは**値の一致**で測ること(存在では測らない)。
|
|
17
|
+
*
|
|
18
|
+
* 優先順位(設計判断・CTO 裁定 2026-08-23):
|
|
19
|
+
* **受領 config の非空値 > アカウントの active_tenant_id > ''**
|
|
20
|
+
* 🔑 `bl_tenant_id` は**明示の指示**、アカウントの active は**既定**である。
|
|
21
|
+
* 明示が既定に負けてはならない。
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ 空文字は「指定なし」として扱い、**下位へフォールバックする**。
|
|
24
|
+
* 空を「指定された」と読むと、現行の壊れ方(空で上書き)をそのまま温存することになる。
|
|
25
|
+
*/
|
|
26
|
+
/** `--mcp-config <path>` / `--mcp-config=<path>` の両形から path を取る。無ければ null。 */
|
|
27
|
+
export declare function parseMcpConfigPathFromArgv(argv: readonly string[]): string | null;
|
|
28
|
+
/**
|
|
29
|
+
* パース済みの MCP config から `mcpServers.bluelamp.env.MUSUHI_TENANT_ID` を取る。
|
|
30
|
+
* 見つからない / 文字列でない / 空白のみ は **すべて `''`**(=指定なし)に倒す。
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractTenantIdFromMcpConfig(raw: unknown): string;
|
|
33
|
+
/**
|
|
34
|
+
* 優先順位を 1 箇所に閉じ込める。**明示(継承)> 既定(アカウント)> 未指定**。
|
|
35
|
+
* 両方空なら `''` を返す(呼び出し側は `process.env` を触らない)。
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveTenantId(inherited: string, active: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* argv から受領 config を読み、テナント指定を取り出す。
|
|
40
|
+
* 読み取りは注入(テスト可能にするため)。**読めない・壊れている場合は `''`** に倒す
|
|
41
|
+
* ——ここで例外を投げると、config が壊れているだけで launcher 全体が起動しなくなる。
|
|
42
|
+
*/
|
|
43
|
+
export declare function readInheritedTenantId(argv: readonly string[], readFile: (p: string) => string): string;
|
|
44
|
+
//# sourceMappingURL=inheritedTenant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inheritedTenant.d.ts","sourceRoot":"","sources":["../../src/lib/inheritedTenant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAgBjF;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAWjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIzE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAC9B,MAAM,CAQR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function parseMcpConfigPathFromArgv(t){for(let e=0;e<t.length;e+=1){const r=t[e];if("string"==typeof r){if("--mcp-config"===r){const r=t[e+1];if("string"==typeof r&&r.length>0&&!r.startsWith("-"))return r;continue}if(r.startsWith("--mcp-config=")){const t=r.slice(13);if(t.length>0)return t}}}return null}function extractTenantIdFromMcpConfig(t){if(!t||"object"!=typeof t)return"";const e=t.mcpServers;if(!e||"object"!=typeof e)return"";const r=e.bluelamp;if(!r||"object"!=typeof r)return"";const n=r.env;if(!n||"object"!=typeof n)return"";const o=n.MUSUHI_TENANT_ID;return"string"!=typeof o?"":o.trim()}function resolveTenantId(t,e){return(t??"").trim()||(e??"").trim()}function readInheritedTenantId(t,e){const r=parseMcpConfigPathFromArgv(t);if(!r)return"";try{return extractTenantIdFromMcpConfig(JSON.parse(e(r)))}catch{return""}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseMcpConfigPathFromArgv=parseMcpConfigPathFromArgv,exports.extractTenantIdFromMcpConfig=extractTenantIdFromMcpConfig,exports.resolveTenantId=resolveTenantId,exports.readInheritedTenantId=readInheritedTenantId;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inheritedTenant.js","sourceRoot":"","sources":["../../src/lib/inheritedTenant.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;AAGH,gEAgBC;AAMD,oEAWC;AAMD,0CAIC;AAOD,sDAWC;AA9DD,8EAA8E;AAC9E,SAAgB,0BAA0B,CAAC,IAAuB;IAChE,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;QACtC,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,8DAA8D;YAC9D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;YACtF,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAgB,4BAA4B,CAAC,GAAY;IACvD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC/C,MAAM,OAAO,GAAI,GAAgC,CAAC,UAAU,CAAC;IAC7D,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACvD,MAAM,QAAQ,GAAI,OAAkC,CAAC,QAAQ,CAAC;IAC9D,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzD,MAAM,GAAG,GAAI,QAA8B,CAAC,GAAG,CAAC;IAChD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAI,GAAsC,CAAC,gBAAgB,CAAC;IACvE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,SAAiB,EAAE,MAAc;IAC/D,MAAM,QAAQ,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,SAAgB,qBAAqB,CACnC,IAAuB,EACvB,QAA+B;IAE/B,MAAM,UAAU,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,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,setServiceAlwaysOn:setServiceAlwaysOn,uninstallMcpService:uninstallMcpService,updateMcpEnv:updateMcpEnv,callMcpToolProxy:callMcpToolProxy}=require("./portal-api-client.cjs"),{logger:logger}=require("../../utils/logger"),{t:t,setLanguage:setLanguage}=require("../../i18n"),{spawn:spawn}=require("child_process"),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}=require("../../auth/TokenManager"),TOKEN=TokenManager.loadTokenStringSync(),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";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: "+(TOKEN?TOKEN.substring(0,20)+"...":"NOT SET")),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,s){this.serviceId=e,this.command=t,this.args=(r||[]).map(e=>"string"==typeof e?e.replace(/^~\//,os.homedir()+"/"):e),this.env={...process.env,...s},this.process=null,this.tools=[],this.requestId=0,this.pending=new Map,this.buffer=""}async start(){return new Promise((e,t)=>{const r=setTimeout(()=>{t(new Error(`Timeout: "${this.serviceId}" failed to start within 30s`)),this.stop()},3e4);try{this.process=spawn(this.command,this.args,{env:this.env,stdio:["pipe","pipe","pipe"]}),this.process.on("error",e=>{clearTimeout(r),t(new Error(`Spawn error for "${this.serviceId}": ${e.message}`))}),this.process.on("exit",(e,t)=>{log(`[ChildMcp] "${this.serviceId}" exited (code=${e}, signal=${t})`),this.pending.forEach(e=>e.reject(new Error("Process exited"))),this.pending.clear()}),this.process.stderr.on("data",e=>{log(`[ChildMcp:${this.serviceId}:stderr] ${e.toString().trim()}`)}),this.process.stdout.on("data",e=>{this.buffer+=e.toString(),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 s=t.tools||[];this.tools=s.map(e=>({...e,name:`${this.serviceId}__${e.name}`,_originalName:e.name})),log(`[ChildMcp] "${this.serviceId}" ready: ${this.tools.length} tools`),clearTimeout(r),e(this.tools)}).catch(e=>{clearTimeout(r),t(e)})}catch(e){clearTimeout(r),t(e)}})}async callTool(e,t){return await this._sendRequest("tools/call",{name:e,arguments:t})}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,s)=>{const o=++this.requestId,n=JSON.stringify({jsonrpc:"2.0",id:o,method:e,params:t})+"\n";this.pending.set(o,{resolve:r,reject:s});try{this.process.stdin.write(n)}catch(e){this.pending.delete(o),s(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}async activateExternal(e,t){const r=new ChildMcpProcess(e,t.command,t.args||[],t.env||{}),s=await r.start();return this.children.set(e,{type:"external",process:r,tools:s,service:t}),s}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,s]of this.children)if("external"===s.type){const r=s.tools.find(t=>t.name===e);if(r)return{found:!0,result:await s.process.callTool(r._originalName,t)}}return{found:!1}}getActiveServiceIds(){return[...this.children.keys()]}}class BlueLampServer{constructor(){this.childManager=new ChildMcpManager,this.server=new Server({name:"bluelamp",version:"2.0.0"},{capabilities:{tools:{listChanged:!0}}}),this.setupHandlers()}checkAuth(){return TOKEN?{ok:!0}:{ok:!1,error:"BlueLamp にログインしてください。ターミナルで `bluelamp1` を一度実行すると ~/.musuhi/portal-token.enc が作成されます。"}}buildToolList(){const e=[{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:"MCPサービスを有効化してツールを追加します。search_mcpで見つけたservice_idを指定してください。always_on: trueで次回以降も自動的に有効化されます。",inputSchema:{type:"object",properties:{service_id:{type:"string",description:"サービスID"},always_on:{type:"boolean",description:"trueにすると常時アクティブ(毎回自動起動)になります"}},required:["service_id"]}},{name:"deactivate_service",description:"MCPサービスの常時アクティブ(alwaysOn)設定を解除します。次回起動時から自動有効化されなくなります。",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:{category:{type:"string",description:"カテゴリでフィルタ(省略可)"}},required:[]}},{name:"install_from_store",description:"MCPストアからMCPをダウンロードしてインストールします。browse_mcp_storeで見つけたmcp_idを指定してください。",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"]}}];if(e.push(...this.childManager.getActiveTools()),this._cloudProxyTools)for(const[t,r]of this._cloudProxyTools)e.push({name:t,description:r.description||`[${r.mcpId}] ${r.toolName}`,inputSchema:r.inputSchema||{type:"object",properties:{}}});return e}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:s}=e.params;if(log(`[CallTool] ${r}`),"load_knowledge"===r){const{keyword:e}=s;log(`[load_knowledge] keyword: ${e}`);try{const r=await getPromptFromPortal(e,TOKEN,LANGUAGE);if(!r||!r.success){const s=r?.error||t("mcp.knowledgeNotFound",{keyword:e});return{content:[{type:"text",text:`❌ ${t("mcp.portalApiFailed",{error:s})}`}],isError:!0}}const s="ja"!==LANGUAGE?"\n\n[Note] The user's preferred language is English.":"";return{content:[{type:"text",text:`✅ ${t("mcp.knowledgeLoadSuccess")}\n\n${r.knowledge}${s}\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n${t("mcp.knowledgeApplied")}`}]}}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(!TOKEN)return{content:[{type:"text",text:"❌ 未認証です。MCPカタログの検索にはログインが必要です。"}],isError:!0};let t=[];try{const e=await searchMcpServices(s.query,TOKEN);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:`🔍 「${s.query}」に一致するサービスはありません。\n\n別のキーワードで試すか、browse_mcp_storeでカタログを確認してください。`}]};let r=`🔍 「${s.query}」の検索結果(${t.length}件)\n\n`;for(const e of t.slice(0,10)){const t=e.id,s=e.service,o=this.childManager.isActive(t),n=s.alwaysOn,i=e.isInstalled?"":" [未インストール]",c=n?" [常時アクティブ]":"";r+=`${n?"🔒":o?"🟢":"⚪"} **${s.name}**${c}${i} (id: \`${t}\`)\n`,r+=` ${s.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,always_on:r}=s,o=await getGatewayConfig(TOKEN);if(!o.success)return{content:[{type:"text",text:`❌ サーバー設定の取得に失敗: ${o.error||"Unknown"}`}],isError:!0};const n=(o.data?.services||[]).find(e=>e.mcpId===t);if(!n)return{content:[{type:"text",text:`❌ サービス "${t}" はインストールされていません。install_from_storeでインストールしてください。`}],isError:!0};if(r&&(setServiceAlwaysOn(t,!0,TOKEN).catch(e=>log(`[activate] Server sync failed: ${e.message}`)),log(`[activate] "${t}" set alwaysOn: true`)),this.childManager.isActive(t))return{content:[{type:"text",text:`ℹ️ "${t}" は既に有効です。${r?"(常時アクティブに設定しました)":""}`}]};if("cloud_proxy"===n.hostingType){const e=Array.isArray(n.tools)?n.tools:[];if(0===e.length)return{content:[{type:"text",text:`❌ "${t}" (cloud_proxy) にツール定義がありません。`}],isError:!0};this._cloudProxyTools=this._cloudProxyTools||new Map;for(const r of e){const e=`${t}__${r.name}`;this._cloudProxyTools.set(e,{mcpId:t,toolName:r.name,description:r.description,inputSchema:r.inputSchema})}log(`[activate] "${t}" (cloud_proxy) → ${e.length} proxy tools registered`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[activate] notification error: ${e.message}`)}const s=e.map(e=>`${t}__${e.name}`).join(", "),o=r?" 🔒常時アクティブ":"";return{content:[{type:"text",text:`✅ "${n.name}" を有効化しました${o}(cloud_proxy ツール${e.length}個追加)\n\n追加ツール: ${s}`}]}}const i=path.join(os.homedir(),".claude","mcp-servers",t),c=n.entryPoint||"dist/index.js",a={name:n.name,description:n.description,type:"external",command:n.command||"node",args:n.args&&n.args.length?n.args:[path.join(i,c)],env:n.env||{}};let l;try{l=await this.childManager.activateExternal(t,a)}catch(e){return log(`[activate] Failed "${t}": ${e.message}`),{content:[{type:"text",text:`❌ "${t}" の起動に失敗しました: ${e.message}`}],isError:!0}}const p=l.map(e=>e.name).join(", "),d=r?" 🔒常時アクティブ":"";log(`[activate] "${t}" → ${l.length} tools${r?" (alwaysOn)":""}. 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:`✅ "${a.name}" を有効化しました${d}(ツール${l.length}個追加)\n\n追加ツール: ${p||"(なし)"}`}]}}if("deactivate_service"===r){const{service_id:e}=s;if(!TOKEN)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};const t=await setServiceAlwaysOn(e,!1,TOKEN);if(!t.success)return{content:[{type:"text",text:`❌ "${e}" の解除に失敗: ${t.error||"Unknown"}`}],isError:!0};log(`[deactivate] "${e}" set alwaysOn: false`);let r=0;if(this._cloudProxyTools){const t=`${e}__`;for(const e of[...this._cloudProxyTools.keys()])e.startsWith(t)&&(this._cloudProxyTools.delete(e),r++);r>0&&log(`[deactivate] "${e}" (cloud_proxy) → ${r} proxy tools removed`)}let o=!1;if(this.childManager.isActive(e)&&(this.childManager.deactivate(e),log(`[deactivate] "${e}" child process stopped`),o=!0),r>0||o)try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[deactivate] notification error: ${e.message}`)}let n=`✅ "${e}" の常時アクティブを解除しました。`;return r>0&&(n+=` cloud_proxy ツール${r}個を削除しました。`),o&&(n+=" 子プロセスを停止しました。"),n+=" 次回起動時から自動有効化されません。",{content:[{type:"text",text:n}]}}if("restart_service"===r){const{service_id:e}=s,t=this.childManager.children.get(e),r=t?.service;if(!r)return{content:[{type:"text",text:`❌ "${e}" は現在アクティブではありません。activate_service で有効化してください。`}],isError:!0};let o,n=r;if(TOKEN)try{const t=await getGatewayConfig(TOKEN);if(t.success){const s=(t.data?.services||[]).find(t=>t.mcpId===e);s&&s.env&&(n={...r,env:s.env},log(`[restart] "${e}" env refreshed from server (keys=${Object.keys(s.env).join(",")||"none"})`))}}catch(e){log(`[restart] env refresh failed (using cached): ${e.message}`)}this.childManager.deactivate(e),log(`[restart] "${e}" stopped`);try{o=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}" → ${o.length} tools. Sending list_changed.`);try{await this.server.notification({method:"notifications/tools/list_changed"})}catch(e){log(`[restart] notification error: ${e.message}`)}const i=o.map(e=>e.name).join(", ");return{content:[{type:"text",text:`🔄 "${n.name}" を再起動しました(ツール${o.length}個)\n\nツール: ${i||"(なし)"}`}]}}if("unregister_service"===r){const{service_id:e}=s;if(!TOKEN)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};this.childManager.isActive(e)&&(this.childManager.deactivate(e),log(`[unregister] "${e}" stopped`));const t=await uninstallMcpService(e,TOKEN);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}=s;if(!TOKEN)return{content:[{type:"text",text:"🔒 未認証です。ログインが必要です。"}],isError:!0};const o=r||{},n=await installMcpService(t,!1,TOKEN,o);return n.success?(log(`[register] "${t}" installed on server`),{content:[{type:"text",text:`✅ サービス "${t}" をサーバーに登録しました。\n\n次のステップ:\n- save_mcp_env で APIキー等を設定\n- activate_service で有効化`}]}):{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}=s;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,TOKEN);return e.success?{content:[{type:"text",text:`🔐 環境変数をサーバーに保存しました\n\nサービス: ${t}\n保存キー: ${Object.keys(r).join(", ")}\n\n※ ローカルには保存されません。次回起動時にサーバーから自動取得されます。`}]}:{content:[{type:"text",text:`❌ サーバー保存失敗: ${e.error}`}],isError:!0}}catch(e){return log(`[save_mcp_env] error: ${e.message}`),{content:[{type:"text",text:`❌ ${e.message}`}],isError:!0}}}if("browse_mcp_store"===r){const{category:e}=s||{};log(`[browse_mcp_store] category=${e||"all"}`);try{const t=e?{category:e}:{},r=await listAvailableMcps(t,TOKEN,LANGUAGE);if(!r||!r.success)return{content:[{type:"text",text:`❌ MCPカタログの取得に失敗: ${r?.error||"Unknown"}`}],isError:!0};const s=r.data||[];if(0===s.length)return{content:[{type:"text",text:"📦 インストール可能なMCPはありません。"}]};let o=`📦 MCPストア一覧 (${s.length}件)\n\n`;return s.forEach(e=>{const t=e.hasAccess?"✅":"🔒",r=e.isOfficial?" [公式]":"";o+=`${t} **${e.name}**${r}\n`,o+=` ID: ${e.mcpId}\n`,o+=` ${e.description}\n`,o+=` カテゴリ: ${e.category} | ツール数: ${e.toolCount}\n\n`}),o+='\nインストールするには: install_from_store(mcp_id: "MCP_ID")',{content:[{type:"text",text:o}]}}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}=s;log(`[install_from_store] mcp_id=${e}`);try{const t=await downloadMcpPackage(e,TOKEN);if(!t||!t.success)return{content:[{type:"text",text:`❌ MCPダウンロードに失敗: ${t?.error||"Unknown"}`}],isError:!0};const r=t.data,s=r.packageSource||{},o=r.files||[];let n=s.hostingType||s.hosting&&s.hosting.type||null;if(TOKEN){let t=!1;try{const r=await getGatewayConfig(TOKEN);if(r.success){const s=(r.data?.services||[]).find(t=>t.mcpId===e);s&&(t=!0===s.alwaysOn,n||(n=s.hostingType))}}catch(e){}const r=await installMcpService(e,t,TOKEN);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「${s.name||e}」をインストールしました(cloud_proxy)\n\n`;return t+="☁ クラウド実行のためローカル展開は不要です\n",s.installNotes&&(t+=`\n📝 ${s.installNotes}\n`),t+=`\nactivate_service(service_id: "${e}") で有効化できます。`,{content:[{type:"text",text:t}]}}if(0===o.length)return{content:[{type:"text",text:"❌ MCPパッケージにファイルが含まれていません。"}],isError:!0};const i=path.join(os.homedir(),".claude","mcp-servers",e);fs.mkdirSync(i,{recursive:!0});for(const e of o){const t=path.join(i,e.path);if(fs.mkdirSync(path.dirname(t),{recursive:!0}),fs.writeFileSync(t,e.content,"utf-8"),"number"==typeof e.mode)try{fs.chmodSync(t,511&e.mode)}catch(e){}}if(log(`[install_from_store] ${o.length} files written to ${i}`),s.needsNpmInstall&&fs.existsSync(path.join(i,"package.json"))){const{execSync:e}=require("child_process");try{e("npm install --production",{cwd:i,timeout:6e4,stdio:"pipe"})}catch(e){return{content:[{type:"text",text:`⚠️ ファイル展開完了、npm install失敗: ${e.message}`}]}}}let c=`✅ MCP「${s.name||e}」をインストールしました\n\n`;c+=`📁 インストール先: ${i}\n`,c+=`📄 ファイル数: ${o.length}\n`,s.installNotes&&(c+=`\n📝 ${s.installNotes}\n`);const a=(s.requiredEnvVars||[]).filter(e=>!process.env[e]);return a.length>0&&(c+="\n⚠️ 以下の環境変数を save_mcp_env で設定してください:\n",a.forEach(e=>{c+=` - ${e}\n`})),c+=`\nactivate_service(service_id: "${e}") で有効化できます。`,{content:[{type:"text",text:c}]}}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,s,TOKEN);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,s);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}}return{content:[{type:"text",text:`❌ Unknown tool: ${r}`}],isError:!0}})}async autoActivate(){if(!TOKEN)return void log("[autoActivate] No token, skipping auto-activation");let e=[];try{const t=await getGatewayConfig(TOKEN);if(!t.success)return void log(`[autoActivate] Server config failed: ${t.error||"Unknown"}. Starting with no services.`);e=t.data?.services||[],log(`[autoActivate] Server config: ${e.length} services`)}catch(e){return void log(`[autoActivate] Server config error: ${e.message}. Starting with no services.`)}for(const t of e)if(t.alwaysOn)if("cloud_proxy"===t.hostingType){const e=Array.isArray(t.tools)?t.tools:[];if(0===e.length){log(`[autoActivate] "${t.mcpId}" (cloud_proxy) has no tools, skipping`);continue}for(const r of e){const e=`${t.mcpId}__${r.name}`;this._cloudProxyTools=this._cloudProxyTools||new Map,this._cloudProxyTools.set(e,{mcpId:t.mcpId,toolName:r.name,description:r.description,inputSchema:r.inputSchema})}log(`[autoActivate] "${t.mcpId}" (cloud_proxy) → ${e.length} proxy tools registered`)}else{const e=path.join(os.homedir(),".claude","mcp-servers",t.mcpId),r=t.entryPoint||"dist/index.js",s={name:t.name,description:t.description,type:"external",command:t.command||"node",args:t.args&&t.args.length?t.args:[path.join(e,r)],env:t.env||{}};log(`[autoActivate] Activating "${t.mcpId}" (${t.hostingType})...`);try{const e=await this.childManager.activateExternal(t.mcpId,s);log(`[autoActivate] "${t.mcpId}" ready: ${e.length} tools`)}catch(e){log(`[autoActivate] "${t.mcpId}" failed: ${e.message}`)}}}async run(){const e=new StdioServerTransport;if(await this.server.connect(e),log("[Gateway] BlueLamp Gateway v2.0.0 started."),await this.autoActivate(),this.childManager.getActiveServiceIds().length>0)try{await this.server.notification({method:"notifications/tools/list_changed"}),log("[Gateway] Initial list_changed sent after autoActivate.")}catch(e){log(`[Gateway] Initial list_changed error: ${e.message}`)}if(KEYWORD&&TOKEN){log(`[Gateway] Auto-loading knowledge for keyword: ${KEYWORD}`);const e=await getPromptFromPortal(KEYWORD,TOKEN,LANGUAGE);e&&e.success?log(`[Gateway] Auto-load successful: ${e.title}`):log(`[Gateway] Auto-load failed for keyword: ${KEYWORD}`)}}}const server=new BlueLampServer;let _blIsShuttingDown=!1;const _blCleanupChildren=e=>{if(!_blIsShuttingDown){_blIsShuttingDown=!0;try{log(`[Gateway] Shutdown (${e}) - cleaning up children...`)}catch(e){}if(server&&server.childManager&&server.childManager.children)for(const[e,t]of server.childManager.children)try{try{log(`[Gateway] Stopping child: ${e}`)}catch(e){}t&&"external"===t.type&&t.process&&"function"==typeof t.process.stop&&t.process.stop()}catch(e){}setTimeout(()=>process.exit(0),200).unref()}};process.on("SIGTERM",()=>_blCleanupChildren("SIGTERM")),process.on("SIGINT",()=>_blCleanupChildren("SIGINT")),process.on("SIGHUP",()=>_blCleanupChildren("SIGHUP")),process.stdin.on("end",()=>_blCleanupChildren("stdin-end")),process.stdin.on("close",()=>_blCleanupChildren("stdin-close")),process.on("exit",()=>{if(server&&server.childManager&&server.childManager.children)for(const[e,t]of server.childManager.children)try{t&&t.process&&t.process.process&&"function"==typeof t.process.process.kill&&t.process.process.kill("SIGKILL")}catch(e){}}),server.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"),{describeTokenForDebug:describeTokenForDebug}=requireFromPackage(require,"../../lib/debugRedaction"),{collectUpdateNotices:collectUpdateNotices,buildUpdateNoticeText:buildUpdateNoticeText}=require("./mcp-version-advisor.cjs"),{installPackageFiles:installPackageFiles,formatInstallFailure:formatInstallFailure}=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("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})}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 BizBoostServer{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:"BizBoost にログインしてください。ターミナルで `bizboost1` を一度実行すると ~/.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=>{const{execSync:t}=require("child_process");t("npm install --production",{cwd:e,timeout:6e4,stdio:"pipe"})}});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] BizBoost 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,BizBoostServer:BizBoostServer,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 BizBoostServer;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)})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const fs=require("fs"),path=require("path"),PRESERVE_ACROSS_SWAP=["node_modules"];function backupPathFor(r,e=new Date){return`${r}.bak-${e.toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"Z")}`}function assertInsideInstallDir(r,e){const t=path.resolve(r),n=path.resolve(e);if(n!==t&&!n.startsWith(t+path.sep))throw new Error(`install: 展開先が installDir の外を指しています(${n})`);return n}function rmrf(r){fs.rmSync(r,{recursive:!0,force:!0})}function installPackageFiles(r){const{installDir:e,files:t,needsNpmInstall:n=!1,runNpmInstall:a,now:s}=r,o=fs.existsSync(e),l=o?backupPathFor(e,s):null;o&&fs.renameSync(e,l);const c=(r,t)=>{try{rmrf(e)}catch(r){}if(!l)return{ok:!1,rolledBack:!1,backupPath:null,stage:r,error:t};try{return fs.renameSync(l,e),{ok:!1,rolledBack:!0,backupPath:null,stage:r,error:t}}catch(e){return{ok:!1,rolledBack:!1,restoreFailed:!0,backupPath:l,stage:r,error:new Error(`${t.message} / さらに旧版の復元にも失敗: ${e.message}`)}}};let i=!1;try{fs.mkdirSync(e,{recursive:!0});for(const r of t){const t=assertInsideInstallDir(e,path.join(e,r.path));if(fs.mkdirSync(path.dirname(t),{recursive:!0}),fs.writeFileSync(t,r.content,"utf-8"),"number"==typeof r.mode)try{fs.chmodSync(t,511&r.mode)}catch(r){}}if(l&&!n)for(const r of PRESERVE_ACROSS_SWAP){const t=path.join(l,r),n=path.join(e,r);fs.existsSync(t)&&!fs.existsSync(n)&&(fs.renameSync(t,n),i=!0)}}catch(r){return c("extract",r)}if(n&&fs.existsSync(path.join(e,"package.json")))try{if("function"!=typeof a)throw new Error("runNpmInstall が渡されていません");a(e)}catch(r){return c("npm-install",r)}if(l)try{rmrf(l)}catch(r){}return{ok:!0,installDir:e,fileCount:t.length,restoredNodeModules:i}}function formatInstallFailure(r,e){const t="npm-install"===e.stage?"npm install":"ファイル展開";return e.restoreFailed?`❌ MCP「${r}」の${t}に失敗し、**旧版の復元にも失敗しました**。\n🚨 この機体の ${r} は壊れています。退避は次の場所に在ります:\n ${e.backupPath}\n詳細: ${e.error.message}`:e.rolledBack?`❌ MCP「${r}」の${t}に失敗したため、**旧版へ戻しました**(機体は元の状態です)。\n詳細: ${e.error.message}`:`❌ MCP「${r}」の${t}に失敗しました(新規インストールのため、作りかけは削除しました)。\n詳細: ${e.error.message}`}module.exports={installPackageFiles:installPackageFiles,formatInstallFailure:formatInstallFailure,backupPathFor:backupPathFor,PRESERVE_ACROSS_SWAP:PRESERVE_ACROSS_SWAP};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const fs=require("fs"),os=require("os"),path=require("path"),STATUS={NOTIFY:"notify",NO_NOTIFY:"no-notify",UNDETERMINABLE:"undeterminable",NOT_APPLICABLE:"not-applicable"},REASON={HOSTING_TYPE_NO_LOCAL_DISK:"hosting-type-no-local-disk",CATALOG_VERSION_UNKNOWN:"catalog-version-unknown",LOCAL_VERSION_UNREADABLE:"local-version-unreadable",UPDATE_AVAILABLE:"update-available",UP_TO_DATE:"up-to-date",LOCAL_NEWER:"local-newer"};function runsFromLocalDisk(e){return"local"===e||"external_api"===e}function parseVersion(e){if("string"!=typeof e)return null;const r=e.trim();if(!r)return null;const t=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(r);return t?{major:Number(t[1]),minor:Number(t[2]),patch:Number(t[3]),prerelease:t[4]?t[4].split("."):null}:null}function comparePrerelease(e,r){if(null===e&&null===r)return 0;if(null===e)return 1;if(null===r)return-1;const t=Math.max(e.length,r.length);for(let o=0;o<t;o+=1){const t=e[o],n=r[o];if(void 0===t)return-1;if(void 0===n)return 1;const s=/^\d+$/.test(t),a=/^\d+$/.test(n);if(s&&a){const e=Number(t)-Number(n);if(0!==e)return e<0?-1:1}else{if(s!==a)return s?-1:1;if(t!==n)return t<n?-1:1}}return 0}function compareVersions(e,r){const t=parseVersion(e),o=parseVersion(r);if(!t||!o)return null;for(const e of["major","minor","patch"])if(t[e]!==o[e])return t[e]<o[e]?-1:1;return comparePrerelease(t.prerelease,o.prerelease)}function evaluateUpdateNotice({mcpId:e,hostingType:r,catalogVersion:t,localVersion:o}){const n={mcpId:e,catalogVersion:"string"==typeof t?t:null,localVersion:"string"==typeof o?o:null};if(!runsFromLocalDisk(r))return{...n,status:STATUS.NOT_APPLICABLE,reason:REASON.HOSTING_TYPE_NO_LOCAL_DISK};const s=parseVersion(t),a=parseVersion(o);if(!s)return{...n,status:STATUS.UNDETERMINABLE,reason:REASON.CATALOG_VERSION_UNKNOWN};if(!a)return{...n,status:STATUS.UNDETERMINABLE,reason:REASON.LOCAL_VERSION_UNREADABLE};const i=compareVersions(t,o);return 1===i?{...n,status:STATUS.NOTIFY,reason:REASON.UPDATE_AVAILABLE}:0===i?{...n,status:STATUS.NO_NOTIFY,reason:REASON.UP_TO_DATE}:{...n,status:STATUS.NO_NOTIFY,reason:REASON.LOCAL_NEWER}}function buildUpdateNoticeText(e){const r=Array.isArray(e)?e:[],t=r.filter(e=>e.status===STATUS.NOTIFY),o=r.filter(e=>e.status===STATUS.UNDETERMINABLE),n=r.filter(e=>e.status===STATUS.NOT_APPLICABLE);if(0===t.length&&0===o.length)return null;const s=["━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","📦 MCP の版チェック(案内のみ・自動更新はしません)"];if(t.length>0){s.push("","⚠️ この機体のインストール実体が、カタログより古いものがあります:");for(const e of t)s.push(` - ${e.mcpId}: この機体 ${e.localVersion} → カタログ ${e.catalogVersion}`);s.push("",` 更新するには以下を実行してください(全 ${t.length} 件):`);for(const e of t)s.push(` install_from_store(mcp_id: "${e.mcpId}")`);s.push(" 反映には新しいセッションが必要です(走行中のセッションは差し替わりません)。")}if(o.length>0){s.push("","❓ 古いかどうか判定できませんでした(材料が欠けています。最新である証拠ではありません):");for(const e of o){const r=e.reason===REASON.CATALOG_VERSION_UNKNOWN?"カタログ側に package_version が入っていない":`この機体のインストール実体が読めない(カタログ ${e.catalogVersion})`;s.push(` - ${e.mcpId}: ${r}`)}}return n.length>0&&s.push("",`(対象外: ${n.length} 件 — cloud_proxy は端末に disk 実体を持たないため比較していません)`),s.join("\n")}function defaultInstallRoot(){return path.join(os.homedir(),".claude","mcp-servers")}function resolveVersionSourcePaths(e){const r=["package.json"];if("string"!=typeof e||!e.trim())return r;const t=e.trim().replace(/\\/g,"/"),o=path.posix.dirname(t);return!o||"."===o||"/"===o||path.posix.isAbsolute(o)||o.split("/").some(e=>".."===e)?r:[`${o}/package.json`,...r]}function readVersionFrom(e){try{const r=JSON.parse(fs.readFileSync(e,"utf8")).version;return"string"==typeof r&&r.trim()?r.trim():null}catch(e){return null}}function readLocalPackageVersion(e,r={}){const t=r.installRoot||defaultInstallRoot(),o=path.resolve(t,e);for(const e of resolveVersionSourcePaths(r.entryPoint)){const r=path.resolve(o,...e.split("/"));if(r!==o&&!r.startsWith(o+path.sep))continue;const t=readVersionFrom(r);if(t)return t}return null}function collectUpdateNotices(e,r={}){const t=r.readLocalVersion||((e,t)=>readLocalPackageVersion(e,{...r,entryPoint:t}));return(Array.isArray(e)?e:[]).map(e=>evaluateUpdateNotice({mcpId:e.mcpId,hostingType:e.hostingType,catalogVersion:e.packageVersion,localVersion:runsFromLocalDisk(e.hostingType)?t(e.mcpId,e.entryPoint):null}))}module.exports={STATUS:STATUS,REASON:REASON,parseVersion:parseVersion,compareVersions:compareVersions,runsFromLocalDisk:runsFromLocalDisk,evaluateUpdateNotice:evaluateUpdateNotice,buildUpdateNoticeText:buildUpdateNoticeText,resolveVersionSourcePaths:resolveVersionSourcePaths,readLocalPackageVersion:readLocalPackageVersion,collectUpdateNotices:collectUpdateNotices};
|
|
@@ -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"),{logger:logger}=require("../../utils/logger"),{t:t}=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] Token (first 30 chars): ${o.substring(0,30)}...`),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});try{const t=JSON.parse(e);t.message&&(o=t.message)}catch(t){}return{success:!1,error:o}}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, returning null"),null;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)return log(`[portal-api-client] Slot config API error: ${s.status}, using default`),null;const a=await s.json();return a.success&&a.data?.keyword?(log(`[portal-api-client] Slot ${t}: custom keyword "${a.data.keyword}"`),a.data.keyword):(log(`[portal-api-client] Slot ${t}: no custom config, using default`),null)}catch(t){return log(`[portal-api-client] getSlotConfig error: ${t.message}, using default`),null}}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.category&&s.set("category",e.category),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 setServiceAlwaysOn(t,e,o){try{if(!o)return{success:!1,error:"No token"};const r=e?"activate":"deactivate",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] setServiceAlwaysOn 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})});return s.ok?{success:!0,data:(await s.json()).data}:{success:!1,error:`HTTP ${s.status}`}}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,setServiceAlwaysOn:setServiceAlwaysOn,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,s="ja"){try{if(log(`[portal-api-client] getPromptFromPortal called with keyword: ${e}, lang: ${s}, token: ${o?"present":"missing"}`),!o)return log("[portal-api-client] No authentication token provided"),{success:!1,error:t("mcp.noAuthToken")};const r=process.env.MUSUHI_TENANT_ID||"",a=encodeURIComponent(e);let n=`${PORTAL_URL}/cli/prompts/knowledge-injection/${a}?lang=${s}`;r&&(n+=`&tenant_id=${encodeURIComponent(r)}`),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()}:{},...r?{"X-Tenant-ID":r}:{},"Content-Type":"application/json","User-Agent":"BizBoost-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}),s=null;try{const t=JSON.parse(e);t.message&&(o=t.message),t.code&&(s=t.code)}catch(t){}return{success:!1,error:o,status:c.status,code:s}}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":"BizBoost-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||"",s=`${PORTAL_URL}/cli/slot-config/${t}`;log(`[portal-api-client] Fetching slot config: ${s}${o?` (tenant=${o})`:""}`);const r=await fetch(s,{method:"GET",headers:{"X-CLI-Token":e,...getCliVersion()?{"X-CLI-Version":getCliVersion()}:{},...o?{"X-Tenant-ID":o}:{},"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0});if(!r.ok){log(`[portal-api-client] Slot config API error: ${r.status}`);let t=null;try{const e=await r.json();t=e?.error?.message||e?.message||null}catch{t=null}return 404===r.status?{ok:!1,reason:"not-configured",status:404,message:t}:403===r.status?{ok:!1,reason:"rank",status:403,message:t}:{ok:!1,reason:"http",status:r.status,message:t}}const a=await r.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,s,r){try{if(log(`[portal-api-client] reportViolation called: severity=${o}`),!s)return{success:!1,error:t("mcp.violationNoToken")};const a=`${PORTAL_URL}/cli/report-violation`,n={reason:e,severity:o};r&&(n.userMessage=r);const c=await fetch(a,{method:"POST",headers:{"X-CLI-Token":s,"Content-Type":"application/json","User-Agent":"BizBoost-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,s="ja"){try{if(log(`[portal-api-client] listAvailableMcps called: ${JSON.stringify(e)}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=new URLSearchParams;e.search&&r.set("search",e.search),e.limit&&r.set("limit",String(e.limit)),r.set("lang",s);const a=`${PORTAL_URL}/mcp/catalog?${r.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":"BizBoost-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,s="ja"){try{if(log(`[portal-api-client] getMcpInfo called: mcpId=${e}`),!o)return{success:!1,error:t("mcp.noAuthToken")};const r=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/details?lang=${s}`;log(`[portal-api-client] Fetching MCP info: ${r}`);const a=await fetch(r,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:r.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,s,r){try{if(log(`[portal-api-client] callMcpTool called: mcpId=${e}, tool=${o}`),!r)return{success:!1,error:t("mcp.noAuthToken")};const a=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/call`,n={tool_name:o,arguments:s};log(`[portal-api-client] Calling MCP tool: ${a}`);const c=await fetch(a,{method:"POST",headers:{"X-CLI-Token":r,"Content-Type":"application/json","User-Agent":"BizBoost-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 s=`${PORTAL_URL}/mcp/scan`,r=await fetch(s,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({code:e})});if(!r.ok){const t=await r.text();return log(`[portal-api-client] scanMcpCode error: ${r.status} ${t}`),{success:!1,error:`HTTP ${r.status}`}}return{success:!0,data:(await r.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 s=`${PORTAL_URL}/mcp/deploy`,r=await fetch(s,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(e)});if(!r.ok){const t=await r.text();log(`[portal-api-client] deployUserMcp error: ${r.status} ${t}`);let e=`HTTP ${r.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 r.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`,s=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:o.startsWith("https")?httpsAgent:void 0});return s.ok?{success:!0,data:(await s.json()).data}:(await s.text(),log(`[portal-api-client] listUserCreatedMcps error: ${s.status}`),{success:!1,error:`HTTP ${s.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 s=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}`,r=await fetch(s,{method:"DELETE",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0});if(!r.ok){const t=await r.text();log(`[portal-api-client] deleteUserMcp error: ${r.status}`);let e=`HTTP ${r.status}`;try{const o=JSON.parse(t);o.error&&(e=o.error)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await r.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`,s=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:o.startsWith("https")?httpsAgent:void 0});return s.ok?{success:!0,data:(await s.json()).data||[]}:(await s.text(),log(`[portal-api-client] listMyPrompts error: ${s.status}`),{success:!1,error:`HTTP ${s.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 s=`${PORTAL_URL}/cli/my-agents`,r=await fetch(s,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify(e)});if(!r.ok){const t=await r.text();log(`[portal-api-client] saveMyPrompt error: ${r.status} ${t}`);let e=`HTTP ${r.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await r.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 s=`${PORTAL_URL}/cli/my-agents/${encodeURIComponent(e)}`,r=await fetch(s,{method:"DELETE",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0});if(!r.ok){const t=await r.text();log(`[portal-api-client] deleteMyPrompt error: ${r.status}`);let e=`HTTP ${r.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}return{success:!0,data:(await r.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 s=`${PORTAL_URL}/mcp/${encodeURIComponent(e)}/download`;log(`[portal-api-client] Downloading MCP package: ${s}`);const r=await fetch(s,{method:"GET",headers:{"X-CLI-Token":o,"Content-Type":"application/json","User-Agent":"BizBoost-VSCode-MCP/1.0"},agent:s.startsWith("https")?httpsAgent:void 0});if(!r.ok){const t=await r.text();log(`[portal-api-client] downloadMcpPackage error: ${r.status} ${t}`);let e=`Download failed: HTTP ${r.status}`;try{const o=JSON.parse(t);o.message&&(e=o.message)}catch(t){}return{success:!1,error:e}}const a=await r.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 s=await o.json();return log(`[portal-api-client] getGatewayConfig: ${s.data?.services?.length||0} services`),{success:!0,data:s.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 s=await fetch(o,{method:"GET",headers:{"X-CLI-Token":e,"Content-Type":"application/json"},agent:o.startsWith("https")?httpsAgent:void 0});return s.ok?{success:!0,data:(await s.json()).data}:{success:!1,error:`HTTP ${s.status}`}}catch(t){return log(`[portal-api-client] searchMcpServices error: ${t.message}`),{success:!1,error:t.message}}}async function installMcpService(t,e,o,s){try{if(!o)return{success:!1,error:"No token"};const r=`${PORTAL_URL}/mcp/user/me/install`,a={mcpId:t,alwaysOn:e??!1};s&&Object.keys(s).length>0&&(a.envOverrides=s);const n=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:r.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 s=e?"deactivate":"activate",r=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}/${s}`,a=await fetch(r,{method:"POST",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:r.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)}`,s=await fetch(o,{method:"DELETE",headers:{"X-CLI-Token":e,"Content-Type":"application/json"},agent:o.startsWith("https")?httpsAgent:void 0});return s.ok?{success:!0,data:(await s.json()).data}:{success:!1,error:`HTTP ${s.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 s=`${PORTAL_URL}/mcp/user/me/services/${encodeURIComponent(t)}/env`,r=await fetch(s,{method:"PUT",headers:{"X-CLI-Token":o,"Content-Type":"application/json"},agent:s.startsWith("https")?httpsAgent:void 0,body:JSON.stringify({env:e})});if(!r.ok)return{success:!1,error:`HTTP ${r.status}`};const a=await r.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,s){try{if(!s)return{success:!1,error:"No token"};const r=`${PORTAL_URL}/mcp/${encodeURIComponent(t)}/call`,a=await fetch(r,{method:"POST",headers:{"X-CLI-Token":s,"Content-Type":"application/json"},agent:r.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,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `~/.mcp.json` の自己修復(P1)。
|
|
3
|
+
*
|
|
4
|
+
* 我々が公開した npm パッケージ `bluelamp` 6.0.2〜6.4.1 の postinstall
|
|
5
|
+
* (`scripts/setup-mcps.js`) が、`~/.mcp.json` に次を書いていた:
|
|
6
|
+
*
|
|
7
|
+
* "bluelamp": { "command": "bluelamp1", "args": [] }
|
|
8
|
+
*
|
|
9
|
+
* `bluelamp1` は MCP サーバーではなく Claude Code 起動ラッパーなので、MCP として
|
|
10
|
+
* 起動されると接続不能になるだけでなく、`npm i -g` 自己更新や
|
|
11
|
+
* `--dangerously-skip-permissions` 付きの入れ子 claude を spawn する経路が開く。
|
|
12
|
+
*
|
|
13
|
+
* 6.4.2 で postinstall は廃止されたが「消す処理」は無かったため、当時インストールした
|
|
14
|
+
* 端末には今も残っている。しかも launcher 経由(`--mcp-config` に正しい定義を渡す)では
|
|
15
|
+
* 同名キーが上書きされて無害化されるため、**素の `claude` を起動した経路だけが壊れる**。
|
|
16
|
+
* 2026-07-30 実測(claude 2.1.220):
|
|
17
|
+
* `--mcp-config` > project scope (`.mcp.json`) > user scope (`~/.claude.json`)
|
|
18
|
+
* SSH ログイン直後の cwd は `$HOME` なので `~/.mcp.json` が project scope として読まれ、
|
|
19
|
+
* user scope の正しい定義を shadow する。これが今日の実害(monster / SSH で詰む)の機構。
|
|
20
|
+
*
|
|
21
|
+
* 🚨 安全要件(主君判断):
|
|
22
|
+
* - 触るのは `mcpServers.bluelamp` の **1 キーだけ**
|
|
23
|
+
* - `command` が我々の writer が書いた形(`bluelamp` + 数字)に一致する場合のみ削除する
|
|
24
|
+
* - 他のキーには一切触らない(同ファイルに残る playwright / password-manager / gmail 等の
|
|
25
|
+
* 古い定義は、他人が手で置いた設定かもしれないので残す)
|
|
26
|
+
* - JSON パースに失敗したファイルは書き戻さない(他人の設定を壊さない)
|
|
27
|
+
*/
|
|
28
|
+
/** 自己修復の対象キー(ゲートウェイ 1 本のみ) */
|
|
29
|
+
export declare const GATEWAY_KEY = "bluelamp";
|
|
30
|
+
export interface McpServerEntry {
|
|
31
|
+
command?: unknown;
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
}
|
|
34
|
+
export interface McpJsonShape {
|
|
35
|
+
mcpServers?: Record<string, McpServerEntry>;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
export type SelfHealSkipReason = 'not-an-object' | 'no-mcp-servers' | 'no-gateway-entry' | 'command-not-ours';
|
|
39
|
+
export interface SelfHealPlan {
|
|
40
|
+
changed: boolean;
|
|
41
|
+
/** 削除した command 値(ログ表示用) */
|
|
42
|
+
removedCommand?: string;
|
|
43
|
+
/** 書き戻すべき次の姿。changed=false のときは undefined */
|
|
44
|
+
next?: McpJsonShape;
|
|
45
|
+
/** changed=false の理由(診断用) */
|
|
46
|
+
reason?: SelfHealSkipReason;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 削除計画を立てる(純関数)。入力は JSON.parse 済みの値。
|
|
50
|
+
* 他キーの順序・内容はそのまま次の姿に引き継ぐ。
|
|
51
|
+
*/
|
|
52
|
+
export declare function planMcpJsonSelfHeal(raw: unknown): SelfHealPlan;
|
|
53
|
+
export interface SelfHealIo {
|
|
54
|
+
exists(filePath: string): boolean;
|
|
55
|
+
read(filePath: string): string;
|
|
56
|
+
write(filePath: string, contents: string): void;
|
|
57
|
+
}
|
|
58
|
+
export type SelfHealOutcome = {
|
|
59
|
+
healed: true;
|
|
60
|
+
removedCommand: string;
|
|
61
|
+
} | {
|
|
62
|
+
healed: false;
|
|
63
|
+
reason: SelfHealSkipReason | 'file-missing' | 'parse-failed' | 'read-failed' | 'write-failed';
|
|
64
|
+
detail?: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* ファイルに対して自己修復を適用する(薄いシェル)。
|
|
68
|
+
* 例外は投げない — 起動を止めないのが最優先で、失敗は理由付きで返す。
|
|
69
|
+
*/
|
|
70
|
+
export declare function healMcpJsonFile(filePath: string, io: SelfHealIo): SelfHealOutcome;
|
|
71
|
+
/** 実機用 IO(呼び出し時に解決する — HOME を差し替えるテストのため事前計算しない) */
|
|
72
|
+
export declare const nodeSelfHealIo: SelfHealIo;
|
|
73
|
+
/** `~/.mcp.json` の絶対パス。呼び出しごとに os.homedir() を解決する */
|
|
74
|
+
export declare function defaultMcpJsonPath(): string;
|
|
75
|
+
/** 実機での自己修復。launcher はこれを起動冒頭で 1 回呼ぶ */
|
|
76
|
+
export declare function healDefaultMcpJson(): SelfHealOutcome;
|
|
77
|
+
//# sourceMappingURL=mcpJsonSelfHeal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpJsonSelfHeal.d.ts","sourceRoot":"","sources":["../../src/lib/mcpJsonSelfHeal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AASH,8BAA8B;AAC9B,eAAO,MAAM,WAAW,aAAa,CAAC;AAEtC,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,kBAAkB,GAC1B,eAAe,GACf,gBAAgB,GAChB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,4BAA4B;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAMD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAwB9D;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,kBAAkB,GAAG,cAAc,GAAG,cAAc,GAAG,aAAa,GAAG,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtI;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,eAAe,CA+BjF;AAMD,oDAAoD;AACpD,eAAO,MAAM,cAAc,EAAE,UAI5B,CAAC;AAEF,qDAAqD;AACrD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wCAAwC;AACxC,wBAAgB,kBAAkB,IAAI,eAAe,CAEpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n);var a=Object.getOwnPropertyDescriptor(t,n);a&&!("get"in a?!t.__esModule:a.writable||a.configurable)||(a={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,a)}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(){var e=function(t){return e=Object.getOwnPropertyNames||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[t.length]=n);return t},e(t)};return function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r=e(t),a=0;a<r.length;a++)"default"!==r[a]&&__createBinding(n,t,r[a]);return __setModuleDefault(n,t),n}}();Object.defineProperty(exports,"__esModule",{value:!0}),exports.nodeSelfHealIo=exports.GATEWAY_KEY=void 0,exports.planMcpJsonSelfHeal=planMcpJsonSelfHeal,exports.healMcpJsonFile=healMcpJsonFile,exports.defaultMcpJsonPath=defaultMcpJsonPath,exports.healDefaultMcpJson=healDefaultMcpJson;const fs=__importStar(require("node:fs")),os=__importStar(require("node:os")),path=__importStar(require("node:path")),OUR_BROKEN_COMMAND=/^bluelamp\d+$/;function isPlainObject(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function planMcpJsonSelfHeal(e){if(!isPlainObject(e))return{changed:!1,reason:"not-an-object"};const t=e.mcpServers;if(!isPlainObject(t))return{changed:!1,reason:"no-mcp-servers"};const n=t[exports.GATEWAY_KEY];if(!isPlainObject(n))return{changed:!1,reason:"no-gateway-entry"};const r=n.command;if("string"!=typeof r||!OUR_BROKEN_COMMAND.test(r))return{changed:!1,reason:"command-not-ours"};const a={};for(const e of Object.keys(t))e!==exports.GATEWAY_KEY&&(a[e]=t[e]);return{changed:!0,removedCommand:r,next:{...e,mcpServers:a}}}function healMcpJsonFile(e,t){if(!t.exists(e))return{healed:!1,reason:"file-missing"};let n,r;try{n=t.read(e)}catch(e){return{healed:!1,reason:"read-failed",detail:messageOf(e)}}try{r=JSON.parse(n)}catch(e){return{healed:!1,reason:"parse-failed",detail:messageOf(e)}}const a=planMcpJsonSelfHeal(r);if(!a.changed||!a.next)return{healed:!1,reason:a.reason??"command-not-ours"};try{t.write(e,`${JSON.stringify(a.next,null,2)}\n`)}catch(e){return{healed:!1,reason:"write-failed",detail:messageOf(e)}}return{healed:!0,removedCommand:a.removedCommand}}function messageOf(e){return e instanceof Error?e.message:String(e)}function defaultMcpJsonPath(){return path.join(os.homedir(),".mcp.json")}function healDefaultMcpJson(){return healMcpJsonFile(defaultMcpJsonPath(),exports.nodeSelfHealIo)}exports.GATEWAY_KEY="bluelamp",exports.nodeSelfHealIo={exists:e=>fs.existsSync(e),read:e=>fs.readFileSync(e,"utf-8"),write:(e,t)=>fs.writeFileSync(e,t,"utf-8")};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpJsonSelfHeal.js","sourceRoot":"","sources":["../../src/lib/mcpJsonSelfHeal.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CH,kDAwBC;AAgBD,0CA+BC;AAcD,gDAEC;AAGD,gDAEC;AAxID,4CAA8B;AAC9B,4CAA8B;AAC9B,gDAAkC;AAElC,qDAAqD;AACrD,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAE3C,8BAA8B;AACjB,QAAA,WAAW,GAAG,UAAU,CAAC;AA4BtC,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,GAAY;IAC9C,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAE5E,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC;IAC/B,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAEjF,MAAM,KAAK,GAAG,OAAO,CAAC,mBAAW,CAAC,CAAC;IACnC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAEjF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACrE,4DAA4D;QAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACxD,CAAC;IAED,kCAAkC;IAClC,MAAM,WAAW,GAAmC,EAAE,CAAC;IACvD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,IAAI,GAAG,KAAK,mBAAW;YAAE,SAAS;QAClC,WAAW,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAmB,CAAC;IACpD,CAAC;IAED,MAAM,IAAI,GAAiB,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;IAC/D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1D,CAAC;AAYD;;;GAGG;AACH,SAAgB,eAAe,CAAC,QAAgB,EAAE,EAAc;IAC9D,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAE3E,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5E,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,uCAAuC;QACvC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7E,CAAC;IAED,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAChC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,kBAAkB,EAAE,CAAC;IACtE,CAAC;IAED,IAAI,CAAC;QACH,qDAAqD;QACrD,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7E,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,cAAwB,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,oDAAoD;AACvC,QAAA,cAAc,GAAe;IACxC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;IAC7C,IAAI,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC;IACtD,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC;CAC7E,CAAC;AAEF,qDAAqD;AACrD,SAAgB,kBAAkB;IAChC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAC9C,CAAC;AAED,wCAAwC;AACxC,SAAgB,kBAAkB;IAChC,OAAO,eAAe,CAAC,kBAAkB,EAAE,EAAE,sBAAc,CAAC,CAAC;AAC/D,CAAC"}
|