metheus-governance-mcp-cli 0.2.96 → 0.2.98

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 CHANGED
@@ -23,7 +23,6 @@ npm install -g metheus-governance-mcp-cli@latest
23
23
 
24
24
  Install creates local provider settings templates here:
25
25
 
26
- - `~/.metheus/telegram.env`
27
26
  - `~/.metheus/telegram-bots/`
28
27
  - `~/.metheus/slack.env`
29
28
  - `~/.metheus/kakaotalk.env`
@@ -32,7 +31,8 @@ Install creates local provider settings templates here:
32
31
  These files are for local provider bot secrets, local transport options, and optional per-bot local AI binding.
33
32
 
34
33
  - Store locally:
35
- - `TELEGRAM_BOT_TOKEN` as a legacy Telegram fallback in `telegram.env`
34
+ - Telegram-wide settings in `~/.metheus/telegram-bots/_global.env`
35
+ - `TELEGRAM_BOT_TOKEN` as a legacy Telegram fallback in `_global.env`
36
36
  - one Telegram bot file per entry in `~/.metheus/telegram-bots/<bot-key>.env`
37
37
  - `SLACK_BOT_TOKEN`
38
38
  - `KAKAOTALK_BOT_TOKEN`
@@ -41,12 +41,12 @@ These files are for local provider bot secrets, local transport options, and opt
41
41
  - `chat_id` / channel / room identifier
42
42
  - label / active state
43
43
  - Do not put project chat destination identifiers in local env files.
44
- - Telegram-wide settings stay in `telegram.env`; Telegram per-bot secrets and AI fields live in `telegram-bots/*.env`.
44
+ - Telegram-wide settings now live in `telegram-bots/_global.env`; Telegram per-bot secrets and AI fields live in `telegram-bots/*.env`.
45
45
 
46
46
  Example templates:
47
47
 
48
48
  ```env
49
- # ~/.metheus/telegram.env
49
+ # ~/.metheus/telegram-bots/_global.env
50
50
  TELEGRAM_API_BASE_URL=
51
51
  TELEGRAM_AUTO_CLEAR_WEBHOOK=true
52
52
  TELEGRAM_ALLOWED_UPDATES=message,edited_message
@@ -164,7 +164,6 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
164
164
 
165
165
  `setup` also ensures local provider templates exist:
166
166
 
167
- - `~/.metheus/telegram.env`
168
167
  - `~/.metheus/telegram-bots/`
169
168
  - `~/.metheus/slack.env`
170
169
  - `~/.metheus/kakaotalk.env`
@@ -172,7 +171,7 @@ metheus-governance-mcp-cli setup --project-id <project_uuid> --ctxpack-key "<ctx
172
171
 
173
172
  When Telegram local config already exists, bootstrap/setup keeps your secrets but auto-normalizes the layout to the latest split structure:
174
173
 
175
- - Telegram-wide settings stay in `~/.metheus/telegram.env`
174
+ - Telegram-wide settings stay in `~/.metheus/telegram-bots/_global.env`
176
175
  - per-bot secrets move to `~/.metheus/telegram-bots/<bot-key>.env`
177
176
  - stale inline keys such as `TELEGRAM_BOT_<NAME>_BOT_*` are rewritten into generic per-bot keys
178
177
 
@@ -183,7 +182,7 @@ Fill provider bot secrets and provider-local transport options locally. Project
183
182
  - which provider/role bot profile to use
184
183
  - which `project_id -> workspace_dir` mapping to apply locally
185
184
  - which role profile maps to which local CLI/model/permission/reasoning policy
186
- - which server bot maps to which local LLM execution profile via `telegram.env` or fallback `bot_bindings`
185
+ - which server bot maps to which local LLM execution profile via `telegram-bots/_global.env`, `telegram-bots/*.env`, or fallback `bot_bindings`
187
186
 
188
187
  Built-in helper command for legacy fallback/testing:
189
188
 
@@ -398,7 +397,7 @@ Recommended production path:
398
397
  - keep `project_mappings.<project_id>.workspace_dir` aligned to that teammate's actual local project folder
399
398
  - let `ctxpack pull` or project connection refresh the mapping automatically
400
399
  - keep per-role execution policy under `role_profiles`
401
- - keep per-bot LLM binding primarily in `~/.metheus/telegram.env`
400
+ - keep Telegram-wide binding defaults in `~/.metheus/telegram-bots/_global.env`
402
401
  - use `bot_bindings` in `bot-runner.json` only as local fallback/override
403
402
  - runner resolution order is: explicit `route.role_profile` -> provider env bot binding -> `bot_bindings` -> server bot role -> `route.role`
404
403
 
@@ -471,7 +470,7 @@ Notes:
471
470
  - `local-bot-bridge` reads stdin JSON from the runner and can call Codex/Claude/Gemini for you
472
471
  - `route.command` fallback is disabled by default; enable it only temporarily with `METHEUS_ALLOW_LEGACY_RUNNER_COMMAND=1`
473
472
  - today this automation path is implemented for Telegram end-to-end
474
- - prefer `TELEGRAM_API_BASE_URL=` inside `~/.metheus/telegram.env` for per-bot local Telegram API overrides; `METHEUS_TELEGRAM_API_BASE_URL` remains a process-level fallback mainly for mock/regression testing
473
+ - prefer `TELEGRAM_API_BASE_URL=` inside `~/.metheus/telegram-bots/_global.env` for local Telegram API overrides; `METHEUS_TELEGRAM_API_BASE_URL` remains a process-level fallback mainly for mock/regression testing
475
474
  - Slack can use direct local send, but automatic inbound runner flow is not completed yet
476
475
  - KakaoTalk config can be stored now, but direct send/runner flow is not implemented yet
477
476
  - `doctor` now reports provider support for both enabled runner routes and active project chat destinations
package/cli.mjs CHANGED
@@ -154,9 +154,11 @@ const AUTH_STORE_RELATIVE_PATH = path.join(".metheus", "governance-mcp-auth.json
154
154
  const BOT_RUNNER_CONFIG_RELATIVE_PATH = path.join(".metheus", "bot-runner.json");
155
155
  const BOT_RUNNER_STATE_RELATIVE_PATH = path.join(".metheus", "bot-runner-state.json");
156
156
  const BOT_RUNNER_CONFIG_VERSION = 2;
157
+ const TELEGRAM_LEGACY_ENV_RELATIVE_PATH = path.join(".metheus", "telegram.env");
158
+ const TELEGRAM_GLOBAL_ENV_RELATIVE_PATH = path.join(".metheus", "telegram-bots", "_global.env");
157
159
  const PROVIDER_ENV_CONFIG = {
158
160
  telegram: {
159
- relativePath: path.join(".metheus", "telegram.env"),
161
+ relativePath: TELEGRAM_GLOBAL_ENV_RELATIVE_PATH,
160
162
  tokenKey: "TELEGRAM_BOT_TOKEN",
161
163
  label: "Telegram",
162
164
  verifyURL: "https://api.telegram.org",
@@ -736,6 +738,10 @@ function providerEnvFilePath(provider) {
736
738
  return resolveHomeFilePath(providerEnvConfig(provider).relativePath);
737
739
  }
738
740
 
741
+ function telegramLegacyEnvFilePath() {
742
+ return resolveHomeFilePath(TELEGRAM_LEGACY_ENV_RELATIVE_PATH);
743
+ }
744
+
739
745
  function telegramBotEntriesDirPath() {
740
746
  return resolveHomeFilePath(".metheus/telegram-bots");
741
747
  }
@@ -751,7 +757,8 @@ function providerEnvTemplate(provider) {
751
757
  return [
752
758
  "# Metheus local Telegram bot settings",
753
759
  "# Keep this file on your machine only. Do not commit it.",
754
- "# Store only Telegram-wide settings here.",
760
+ "# Store Telegram-wide settings here.",
761
+ "# This global file now lives in ~/.metheus/telegram-bots/_global.env.",
755
762
  "# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<bot-key>.env.",
756
763
  "# Project chat destinations must be managed on the Metheus server.",
757
764
  "",
@@ -1037,6 +1044,23 @@ function isCompatibleLegacyLocalBotBridgeCommand(command, roleProfile) {
1037
1044
  }
1038
1045
 
1039
1046
  function ensureProviderEnvTemplate(provider) {
1047
+ if (normalizeBotProvider(provider) === "telegram") {
1048
+ const filePath = providerEnvFilePath(provider);
1049
+ const legacyFilePath = telegramLegacyEnvFilePath();
1050
+ const existedBefore = fs.existsSync(filePath) || fs.existsSync(legacyFilePath);
1051
+ try {
1052
+ const state = readTelegramEnvState();
1053
+ writeTelegramEnvState(state.parsed);
1054
+ return {
1055
+ filePath,
1056
+ created: !existedBefore,
1057
+ existed: existedBefore,
1058
+ normalized: true,
1059
+ };
1060
+ } catch (err) {
1061
+ return { filePath, created: false, existed: false, error: String(err?.message || err) };
1062
+ }
1063
+ }
1040
1064
  const filePath = providerEnvFilePath(provider);
1041
1065
  try {
1042
1066
  if (fs.existsSync(filePath)) {
@@ -2737,7 +2761,7 @@ function loadTelegramBotEntriesFromFiles() {
2737
2761
  const dirPath = telegramBotEntriesDirPath();
2738
2762
  if (!fs.existsSync(dirPath)) return [];
2739
2763
  return fs.readdirSync(dirPath, { withFileTypes: true })
2740
- .filter((item) => item.isFile() && /\.env$/i.test(item.name))
2764
+ .filter((item) => item.isFile() && /\.env$/i.test(item.name) && item.name.toLowerCase() !== "_global.env")
2741
2765
  .map((item) => path.join(dirPath, item.name))
2742
2766
  .sort((left, right) => left.localeCompare(right))
2743
2767
  .map((filePath) => {
@@ -2775,14 +2799,20 @@ function buildMergedTelegramEnvParsed(globalParsed, entries) {
2775
2799
 
2776
2800
  function readTelegramEnvState() {
2777
2801
  const filePath = providerEnvFilePath("telegram");
2802
+ const legacyFilePath = telegramLegacyEnvFilePath();
2803
+ let sourceFilePath = filePath;
2778
2804
  try {
2779
- if (!fs.existsSync(filePath)) {
2805
+ if (!fs.existsSync(filePath) && fs.existsSync(legacyFilePath)) {
2806
+ sourceFilePath = legacyFilePath;
2807
+ } else if (!fs.existsSync(filePath)) {
2780
2808
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
2781
2809
  fs.writeFileSync(filePath, providerEnvTemplate("telegram"), "utf8");
2782
2810
  }
2783
2811
  } catch (err) {
2784
2812
  return {
2785
2813
  filePath,
2814
+ legacyFilePath,
2815
+ sourceFilePath,
2786
2816
  entriesDirPath: telegramBotEntriesDirPath(),
2787
2817
  parsed: {},
2788
2818
  globalParsed: {},
@@ -2790,7 +2820,7 @@ function readTelegramEnvState() {
2790
2820
  error: String(err?.message || err),
2791
2821
  };
2792
2822
  }
2793
- const raw = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
2823
+ const raw = fs.existsSync(sourceFilePath) ? fs.readFileSync(sourceFilePath, "utf8") : "";
2794
2824
  const globalParsedRaw = parseSimpleEnvText(raw);
2795
2825
  const inlineEntries = collectTelegramEnvBotEntries(globalParsedRaw);
2796
2826
  const fileEntries = loadTelegramBotEntriesFromFiles();
@@ -2806,6 +2836,8 @@ function readTelegramEnvState() {
2806
2836
  const entries = Array.from(mergedEntries.values());
2807
2837
  return {
2808
2838
  filePath,
2839
+ legacyFilePath,
2840
+ sourceFilePath,
2809
2841
  entriesDirPath: telegramBotEntriesDirPath(),
2810
2842
  parsed: buildMergedTelegramEnvParsed(globalParsed, entries),
2811
2843
  globalParsed,
@@ -2833,7 +2865,8 @@ function renderNormalizedTelegramEnv(parsedEnv) {
2833
2865
  const lines = [
2834
2866
  "# Metheus local Telegram bot settings",
2835
2867
  "# Keep this file on your machine only. Do not commit it.",
2836
- "# Store only Telegram-wide settings here.",
2868
+ "# Store Telegram-wide settings here.",
2869
+ "# This global file lives in ~/.metheus/telegram-bots/_global.env.",
2837
2870
  "# Per-bot secrets and AI settings live in ~/.metheus/telegram-bots/<bot-key>.env.",
2838
2871
  "",
2839
2872
  `TELEGRAM_API_BASE_URL=${formatProviderEnvValue(parsed.TELEGRAM_API_BASE_URL || "")}`,
@@ -2906,6 +2939,7 @@ function writeTelegramEnvState(parsedEnv) {
2906
2939
  }
2907
2940
  });
2908
2941
  const globalFilePath = providerEnvFilePath("telegram");
2942
+ const legacyFilePath = telegramLegacyEnvFilePath();
2909
2943
  fs.mkdirSync(path.dirname(globalFilePath), { recursive: true });
2910
2944
  fs.writeFileSync(globalFilePath, renderNormalizedTelegramEnv(globalParsed), {
2911
2945
  encoding: "utf8",
@@ -2923,13 +2957,16 @@ function writeTelegramEnvState(parsedEnv) {
2923
2957
  });
2924
2958
  });
2925
2959
  fs.readdirSync(entriesDirPath, { withFileTypes: true })
2926
- .filter((item) => item.isFile() && /\.env$/i.test(item.name))
2960
+ .filter((item) => item.isFile() && /\.env$/i.test(item.name) && item.name.toLowerCase() !== "_global.env")
2927
2961
  .forEach((item) => {
2928
2962
  const filePath = path.resolve(path.join(entriesDirPath, item.name));
2929
2963
  if (!activeFiles.has(filePath)) {
2930
2964
  fs.rmSync(filePath, { force: true });
2931
2965
  }
2932
2966
  });
2967
+ if (legacyFilePath !== globalFilePath && fs.existsSync(legacyFilePath)) {
2968
+ fs.rmSync(legacyFilePath, { force: true });
2969
+ }
2933
2970
  return globalFilePath;
2934
2971
  }
2935
2972
 
@@ -3622,8 +3659,10 @@ function buildBotCommandDeps() {
3622
3659
  loadBotRunnerConfig,
3623
3660
  saveBotRunnerConfig,
3624
3661
  listServerBots: async ({ provider, baseURL, timeoutSeconds }) => {
3625
- const authFlowDeps = buildAuthFlowDeps();
3626
- const resolved = resolveCurrentAccessToken(authFlowDeps);
3662
+ const resolved = await resolveAccessTokenForCommand(
3663
+ baseURL || DEFAULT_SITE_URL,
3664
+ intFromRaw(timeoutSeconds, 15),
3665
+ );
3627
3666
  if (!resolved.token) {
3628
3667
  return {
3629
3668
  ok: false,
@@ -5372,7 +5411,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
5372
5411
  try {
5373
5412
  process.env.USERPROFILE = telegramEnvNormalizeTempHome;
5374
5413
  process.env.HOME = telegramEnvNormalizeTempHome;
5375
- const normalizeTestEnvPath = providerEnvFilePath("telegram");
5414
+ const normalizeTestEnvPath = telegramLegacyEnvFilePath();
5376
5415
  fs.mkdirSync(path.dirname(normalizeTestEnvPath), { recursive: true });
5377
5416
  fs.writeFileSync(
5378
5417
  normalizeTestEnvPath,
@@ -5385,7 +5424,7 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
5385
5424
  "utf8",
5386
5425
  );
5387
5426
  ensureProviderEnvTemplate("telegram");
5388
- normalizedGlobalText = fs.readFileSync(normalizeTestEnvPath, "utf8");
5427
+ normalizedGlobalText = fs.readFileSync(providerEnvFilePath("telegram"), "utf8");
5389
5428
  normalizedBotText = fs.readFileSync(telegramBotEntryFilePath("ryoai"), "utf8");
5390
5429
  } finally {
5391
5430
  if (previousUserProfile === undefined) delete process.env.USERPROFILE;
@@ -288,8 +288,8 @@ export async function runSelftestBotCommands(push, deps) {
288
288
  const env = buildSpawnEnv(tempHome);
289
289
  const baseURL = `http://127.0.0.1:${mock.port}`;
290
290
  const telegramApiBaseURL = `${baseURL}/telegram`;
291
- const telegramEnvPath = path.join(tempHome, ".metheus", "telegram.env");
292
291
  const telegramBotEntriesDir = path.join(tempHome, ".metheus", "telegram-bots");
292
+ const telegramEnvPath = path.join(telegramBotEntriesDir, "_global.env");
293
293
  const readTelegramGlobals = () => parseSimpleEnvText(fs.readFileSync(telegramEnvPath, "utf8"));
294
294
  const readTelegramBotEntry = (botKey) => {
295
295
  const filePath = path.join(telegramBotEntriesDir, `${String(botKey || "").trim()}.env`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.96",
3
+ "version": "0.2.98",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [