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 +8 -9
- package/cli.mjs +50 -11
- package/lib/selftest-bot-commands.mjs +1 -1
- package/package.json +1 -1
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
3626
|
-
|
|
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 =
|
|
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(
|
|
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`);
|