metheus-governance-mcp-cli 0.2.288 → 0.2.290

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
@@ -379,7 +379,7 @@ Behavior:
379
379
  - `bot room-audit` now suggests executable runner routes for the managed server+local intersection. Room visibility is still reported, but a bot no longer has to appear in the admin list before route automation can prepare it.
380
380
  - `bot room-audit` defaults to `monitor` only unless you pass `--role` or `--roles`.
381
381
  - `bot room-audit --apply true` writes missing suggested routes into `~/.metheus/bot-runner.json` and disables overlapping enabled routes in the same project/provider/destination/bot scope that are outside the selected role set.
382
- - `runner project up` is the one-command direct CLI path for Telegram project operations: it runs the same room audit, applies the selected role routes, and starts detached polling by default unless you explicitly pass `--start false`.
382
+ - `runner project up` is the direct CLI preparation path for Telegram project operations: it runs the same room audit and applies the selected role routes. Use `runner start-detached` for persistent polling, or let the TUI call `runner.project_up` to bootstrap detached polling in one step.
383
383
  - `runner project up` can be narrowed with `--bot-name`, `--bot-id`, `--role`, or `--roles <csv>` when you do not want every suggested role route for that room.
384
384
  - `bot remove` without flags starts a guided numbered flow: provider -> bot entry -> confirm removal.
385
385
  - Telegram stores one bot file per entry under `~/.metheus/telegram-bots/<ServerBotName>.env` with generic fields:
@@ -421,7 +421,6 @@ metheus-governance-mcp-cli bot remove --provider telegram --bot-name <server_bot
421
421
  metheus-governance-mcp-cli bot verify --provider telegram --bot-name <server_bot_name> --json true
422
422
  metheus-governance-mcp-cli bot room-audit --provider telegram --project-id <project_uuid> --destination-label <room_label> --json true
423
423
  metheus-governance-mcp-cli bot room-audit --provider telegram --project-id <project_uuid> --destination-label <room_label> --apply true --json true
424
- metheus-governance-mcp-cli runner project up --project-id <project_uuid> --provider telegram --destination-label <room_label>
425
424
  metheus-governance-mcp-cli runner project up --project-id <project_uuid> --provider telegram --destination-label <room_label> --start false
426
425
  metheus-governance-mcp-cli runner start-detached --project-id <project_uuid> --provider telegram --destination-label <room_label>
427
426
  metheus-governance-mcp-cli runner project up --project-id <project_uuid> --provider telegram --destination-label <room_label> --bot-name <server_bot_name> --roles monitor,review --start false
@@ -556,7 +555,7 @@ metheus-governance-mcp-cli runner start --route-name telegram-monitor --concurre
556
555
  Route management:
557
556
  - `runner route add` creates one executable route by selecting the project, provider, role, server bot, and project chat destination in order.
558
557
  - `runner route add` now auto-uses the suggested route name, `5000` ms poll interval, and `enabled=true` unless you pass explicit flags or edit the route later.
559
- - `runner project up` is the shortest practical route bootstrap path for Telegram: it audits one project destination, writes any missing suggested routes, and starts detached polling by default unless you explicitly pass `--start false`.
558
+ - `runner project up` is the shortest practical direct CLI preparation path for Telegram: it audits one project destination and writes any missing suggested routes. Start persistent polling separately with `runner start-detached`, or use the TUI/local tool path that bootstraps detached polling for you.
560
559
  - if room visibility probe fails but one or more enabled routes already exist for that project destination, `runner project up` now treats the probe failure as a warning and can still start those existing routes.
561
560
  - `runner project up --dry-run-delivery true` lets the started runner validate route execution without sending a real provider message.
562
561
  - In public Telegram bot conversations, the stored route role is treated as a hint only. Live room context, the current human request, and recent bot replies take priority over the stored route role hint when the local AI client decides how to answer.
@@ -569,7 +568,6 @@ Route management:
569
568
  Recommended operational path:
570
569
 
571
570
  ```bash
572
- metheus-governance-mcp-cli runner project up --project-id <project_uuid> --provider telegram --destination-label <room_label>
573
571
  metheus-governance-mcp-cli runner project up --project-id <project_uuid> --provider telegram --destination-label <room_label> --start false
574
572
  metheus-governance-mcp-cli runner start-detached --project-id <project_uuid> --provider telegram --destination-label <room_label>
575
573
  metheus-governance-mcp-cli runner project up --project-id <project_uuid> --provider telegram --destination-label <room_label> --bot-name <server_bot_name> --roles monitor,review --start false
package/cli.mjs CHANGED
@@ -12292,17 +12292,21 @@ function resolveRunnerProjectUpExecutionPolicy(flags = {}) {
12292
12292
  const applyRequested = Object.prototype.hasOwnProperty.call(flags, "apply")
12293
12293
  ? boolFromRaw(flags.apply, true)
12294
12294
  : true;
12295
- const explicitStartDetachedRequested = Object.prototype.hasOwnProperty.call(flags, "start-detached");
12295
+ const explicitStartDetachedRequested = Object.prototype.hasOwnProperty.call(flags, "start-detached")
12296
+ || Object.prototype.hasOwnProperty.call(flags, "start_detached");
12296
12297
  const explicitDetachedAliasRequested = Object.prototype.hasOwnProperty.call(flags, "detached");
12297
12298
  const explicitStartRequested = Object.prototype.hasOwnProperty.call(flags, "start");
12298
- const startRequested = Object.prototype.hasOwnProperty.call(flags, "start")
12299
+ const startRequested = explicitStartRequested
12299
12300
  ? boolFromRaw(flags.start, true)
12300
12301
  : false;
12302
+ const startDetachedRaw = Object.prototype.hasOwnProperty.call(flags, "start-detached")
12303
+ ? flags["start-detached"]
12304
+ : flags.start_detached;
12301
12305
  const startDetachedRequested = explicitStartDetachedRequested
12302
- ? boolFromRaw(flags["start-detached"], true)
12306
+ ? boolFromRaw(startDetachedRaw, true)
12303
12307
  : (explicitDetachedAliasRequested
12304
12308
  ? boolFromRaw(flags.detached, false)
12305
- : !explicitStartRequested);
12309
+ : false);
12306
12310
  const shouldStartRunner = startRequested || startDetachedRequested;
12307
12311
  return {
12308
12312
  applyRequested,
@@ -12319,6 +12323,24 @@ function buildRunnerStartDetachedCommand(flags = {}) {
12319
12323
  })].join(" ");
12320
12324
  }
12321
12325
 
12326
+ function sleepSyncMilliseconds(durationMs) {
12327
+ const numericDurationMs = Math.max(0, intFromRaw(durationMs, 0) || 0);
12328
+ if (numericDurationMs <= 0) return;
12329
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, numericDurationMs);
12330
+ }
12331
+
12332
+ function ensureDetachedRunnerBootstrapped(childPID, logFilePath = "") {
12333
+ sleepSyncMilliseconds(1200);
12334
+ if (isProcessAlive(childPID)) {
12335
+ return;
12336
+ }
12337
+ const normalizedLogFilePath = String(logFilePath || "").trim();
12338
+ if (normalizedLogFilePath && fs.existsSync(normalizedLogFilePath)) {
12339
+ throw new Error(`detached runner exited during bootstrap; inspect ${normalizedLogFilePath}`);
12340
+ }
12341
+ throw new Error("detached runner exited during bootstrap before the polling loop became stable");
12342
+ }
12343
+
12322
12344
  function buildRunnerProjectUpNextSteps({
12323
12345
  applyRequested,
12324
12346
  shouldStartRunner,
@@ -12350,7 +12372,7 @@ async function buildRunnerProjectUpResult(flags = {}) {
12350
12372
  const ui = createPrompter();
12351
12373
  try {
12352
12374
  if (shouldRenderPromptChrome(flags)) {
12353
- ui.setFlow("RUNNER PROJECT UP", "Audit one project destination, create missing runner routes, and start detached polling by default unless --start false is passed");
12375
+ ui.setFlow("RUNNER PROJECT UP", "Audit one project destination, create missing runner routes, and optionally start polling when explicitly requested");
12354
12376
  }
12355
12377
  const provider = String(flags.provider || "").trim()
12356
12378
  ? normalizeBotProvider(flags.provider)
@@ -12448,7 +12470,7 @@ async function buildRunnerProjectUpResult(flags = {}) {
12448
12470
  route_apply_changed: Boolean(applyResult.changed),
12449
12471
  route_config_file: String(applyResult.filePath || auditPayload.routeSuggestionConfigFilePath || "-").trim() || "-",
12450
12472
  enabled_routes_for_selection: matchingRoutes.map((route) => normalizeRunnerRoute(route).name).filter(Boolean),
12451
- start_requested: shouldStartRunner,
12473
+ start_requested: startRequested,
12452
12474
  start_detached_requested: startDetachedRequested,
12453
12475
  next_steps: [],
12454
12476
  applied_routes: ensureArray(applyResult.appliedRoutes).map((item) => safeObject(item)),
@@ -12844,12 +12866,13 @@ async function launchDetachedRunnerProcess(flags, routes, sourceCommand) {
12844
12866
  if (launched.status !== 0) {
12845
12867
  throw new Error(String(launched.stderr || launched.stdout || "failed to launch detached runner").trim());
12846
12868
  }
12847
- const childPID = intFromRawAllowZero(String(launched.stdout || "").trim().split(/\r?\n/).pop(), 0);
12848
- if (childPID <= 0) {
12849
- throw new Error("detached runner launch did not return a child process id");
12850
- }
12851
- return buildRunnerDetachedLaunchRecord(childPID, routes, startFlags, sourceCommand, detachedLogFilePath);
12852
- }
12869
+ const childPID = intFromRawAllowZero(String(launched.stdout || "").trim().split(/\r?\n/).pop(), 0);
12870
+ if (childPID <= 0) {
12871
+ throw new Error("detached runner launch did not return a child process id");
12872
+ }
12873
+ ensureDetachedRunnerBootstrapped(childPID, detachedLogFilePath);
12874
+ return buildRunnerDetachedLaunchRecord(childPID, routes, startFlags, sourceCommand, detachedLogFilePath);
12875
+ }
12853
12876
  const launchTempDir = fs.mkdtempSync(path.join(os.tmpdir(), "metheus-runner-launch-"));
12854
12877
  const scriptPath = path.join(launchTempDir, "start-runner.sh");
12855
12878
  const pidFilePath = path.join(launchTempDir, "runner.pid");
@@ -12873,13 +12896,14 @@ async function launchDetachedRunnerProcess(flags, routes, sourceCommand) {
12873
12896
  });
12874
12897
  if (launched.error) {
12875
12898
  throw launched.error;
12876
- }
12877
- if (launched.status !== 0) {
12878
- throw new Error(String(launched.stderr || launched.stdout || "failed to launch detached runner in Terminal.app").trim());
12879
- }
12880
- const childPID = await waitForRunnerPIDFile(pidFilePath);
12881
- return buildRunnerDetachedLaunchRecord(childPID, routes, startFlags, sourceCommand, detachedLogFilePath);
12882
- }
12899
+ }
12900
+ if (launched.status !== 0) {
12901
+ throw new Error(String(launched.stderr || launched.stdout || "failed to launch detached runner in Terminal.app").trim());
12902
+ }
12903
+ const childPID = await waitForRunnerPIDFile(pidFilePath);
12904
+ ensureDetachedRunnerBootstrapped(childPID, detachedLogFilePath);
12905
+ return buildRunnerDetachedLaunchRecord(childPID, routes, startFlags, sourceCommand, detachedLogFilePath);
12906
+ }
12883
12907
  const terminalLauncher = resolveLinuxDetachedTerminalLauncher(scriptPath);
12884
12908
  if (!terminalLauncher) {
12885
12909
  throw new Error("no supported Linux terminal emulator was found (tried x-terminal-emulator, gnome-terminal, konsole, mate-terminal, tilix, alacritty, xterm). Install one of those terminals so runner.start_detached can open an independent window");
@@ -12890,12 +12914,13 @@ async function launchDetachedRunnerProcess(flags, routes, sourceCommand) {
12890
12914
  stdio: "ignore",
12891
12915
  });
12892
12916
  child.unref();
12893
- if (!child.pid) {
12894
- throw new Error("detached terminal launcher did not return a process id");
12895
- }
12896
- const runnerPID = await waitForRunnerPIDFile(pidFilePath);
12897
- return buildRunnerDetachedLaunchRecord(runnerPID, routes, startFlags, sourceCommand, detachedLogFilePath);
12898
- } finally {
12917
+ if (!child.pid) {
12918
+ throw new Error("detached terminal launcher did not return a process id");
12919
+ }
12920
+ const runnerPID = await waitForRunnerPIDFile(pidFilePath);
12921
+ ensureDetachedRunnerBootstrapped(runnerPID, detachedLogFilePath);
12922
+ return buildRunnerDetachedLaunchRecord(runnerPID, routes, startFlags, sourceCommand, detachedLogFilePath);
12923
+ } finally {
12899
12924
  try {
12900
12925
  fs.rmSync(launchTempDir, { recursive: true, force: true });
12901
12926
  } catch {}
@@ -16970,7 +16995,7 @@ function buildLocalToolSpecs() {
16970
16995
  {
16971
16996
  name: "runner.project_up",
16972
16997
  description:
16973
- "Audit one project destination, create missing runner routes, and optionally start polling only when explicitly requested. Use this before runner.start_detached when the route is not ready yet.",
16998
+ "Audit one project destination, create missing runner routes, and let front-end/TUI flows bootstrap detached polling in one step. Pass start=false and start_detached=false for prepare-only mode, or use runner.start_detached directly when routes are already ready.",
16974
16999
  inputSchema: buildRunnerProjectUpInputSchema(),
16975
17000
  },
16976
17001
  {
@@ -19330,15 +19355,15 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
19330
19355
  try {
19331
19356
  const projectUpPolicy = resolveRunnerProjectUpExecutionPolicy({});
19332
19357
  push(
19333
- "runner_project_up_default_policy_prefers_detached_start",
19358
+ "runner_project_up_default_policy_keeps_prepare_only_for_direct_cli",
19334
19359
  projectUpPolicy.applyRequested === true
19335
19360
  && projectUpPolicy.startRequested === false
19336
- && projectUpPolicy.startDetachedRequested === true
19337
- && projectUpPolicy.shouldStartRunner === true,
19361
+ && projectUpPolicy.startDetachedRequested === false
19362
+ && projectUpPolicy.shouldStartRunner === false,
19338
19363
  `apply=${projectUpPolicy.applyRequested} start=${projectUpPolicy.startRequested} detached=${projectUpPolicy.startDetachedRequested} shouldStart=${projectUpPolicy.shouldStartRunner}`,
19339
19364
  );
19340
19365
  } catch (err) {
19341
- push("runner_project_up_default_policy_prefers_detached_start", false, String(err?.message || err));
19366
+ push("runner_project_up_default_policy_keeps_prepare_only_for_direct_cli", false, String(err?.message || err));
19342
19367
  }
19343
19368
 
19344
19369
  try {
@@ -19407,10 +19432,24 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
19407
19432
  push("runner_project_up_probe_failure_blocks_without_existing_route", false, String(err?.message || err));
19408
19433
  }
19409
19434
 
19410
- try {
19411
- const projectUpResponse = await handleLocalProjectToolDispatchImpl(
19412
- {
19413
- requestObj: {
19435
+ try {
19436
+ const projectUpPolicy = resolveRunnerProjectUpExecutionPolicy({ start_detached: true });
19437
+ push(
19438
+ "runner_project_up_tool_flag_accepts_start_detached_snake_case",
19439
+ projectUpPolicy.applyRequested === true
19440
+ && projectUpPolicy.startRequested === false
19441
+ && projectUpPolicy.startDetachedRequested === true
19442
+ && projectUpPolicy.shouldStartRunner === true,
19443
+ `apply=${projectUpPolicy.applyRequested} start=${projectUpPolicy.startRequested} detached=${projectUpPolicy.startDetachedRequested} shouldStart=${projectUpPolicy.shouldStartRunner}`,
19444
+ );
19445
+ } catch (err) {
19446
+ push("runner_project_up_tool_flag_accepts_start_detached_snake_case", false, String(err?.message || err));
19447
+ }
19448
+
19449
+ try {
19450
+ const projectUpResponse = await handleLocalProjectToolDispatchImpl(
19451
+ {
19452
+ requestObj: {
19414
19453
  jsonrpc: "2.0",
19415
19454
  id: 8,
19416
19455
  method: "tools/call",
@@ -19436,36 +19475,56 @@ TELEGRAM_BOT_REVIEW_TOKEN=review-token
19436
19475
  workspaceSignalTrusted: true,
19437
19476
  },
19438
19477
  {
19439
- ...buildLocalProjectDispatchDeps(),
19440
- buildRunnerProjectUpResult: async () => ({
19441
- summaryPayload: {
19478
+ ...buildLocalProjectDispatchDeps(),
19479
+ buildRunnerProjectUpResult: async () => ({
19480
+ summaryPayload: {
19442
19481
  ok: true,
19443
19482
  project_id: selftestProjectID,
19444
19483
  destination_label: "Selftest Room",
19445
- destination_id: "dest-1",
19446
- route_apply_requested: true,
19447
- route_apply_changed: true,
19448
- enabled_routes_for_selection: ["telegram-monitor-selftest"],
19449
- next_steps: [`${CLI_NAME} runner show --route-name telegram-monitor-selftest`],
19450
- },
19451
- matchingRoutes: [{ name: "telegram-monitor-selftest" }],
19452
- applyRequested: true,
19453
- applyResult: { ok: true, changed: true },
19454
- shouldStartRunner: false,
19455
- startDetachedRequested: false,
19456
- startFlags: {},
19457
- }),
19458
- },
19459
- );
19460
- const projectUpStructured = safeObject(safeObject(projectUpResponse).result)?.structuredContent || {};
19461
- push(
19462
- "local_runner_project_up_tool_dispatches",
19463
- projectUpStructured.ok === true
19464
- && ensureArray(projectUpStructured.enabled_routes_for_selection).includes("telegram-monitor-selftest"),
19465
- `routes=${ensureArray(projectUpStructured.enabled_routes_for_selection).join(",")} next=${ensureArray(projectUpStructured.next_steps).join(" | ")}`,
19466
- );
19467
- } catch (err) {
19468
- push("local_runner_project_up_tool_dispatches", false, String(err?.message || err));
19484
+ destination_id: "dest-1",
19485
+ route_apply_requested: true,
19486
+ route_apply_changed: true,
19487
+ enabled_routes_for_selection: ["telegram-monitor-selftest"],
19488
+ next_steps: [`${CLI_NAME} runner show --route-name telegram-monitor-selftest`],
19489
+ start_detached_requested: true,
19490
+ },
19491
+ matchingRoutes: [{ name: "telegram-monitor-selftest" }],
19492
+ applyRequested: true,
19493
+ applyResult: { ok: true, changed: true },
19494
+ shouldStartRunner: true,
19495
+ startDetachedRequested: true,
19496
+ startFlags: {
19497
+ "project-id": selftestProjectID,
19498
+ provider: "telegram",
19499
+ "destination-id": "dest-1",
19500
+ },
19501
+ applyFailureBlocksStart: false,
19502
+ }),
19503
+ startDetachedRunnerWithFlags: async () => ({
19504
+ ok: true,
19505
+ already_running: true,
19506
+ registry_file: "registry.json",
19507
+ launch: {
19508
+ launch_id: "launch-project-up-1",
19509
+ pid: 4242,
19510
+ project_ids: [selftestProjectID],
19511
+ route_names: ["telegram-monitor-selftest"],
19512
+ destination_labels: ["Selftest Room"],
19513
+ },
19514
+ }),
19515
+ },
19516
+ );
19517
+ const projectUpStructured = safeObject(safeObject(projectUpResponse).result)?.structuredContent || {};
19518
+ push(
19519
+ "local_runner_project_up_tool_dispatches",
19520
+ projectUpStructured.ok === true
19521
+ && ensureArray(projectUpStructured.enabled_routes_for_selection).includes("telegram-monitor-selftest")
19522
+ && projectUpStructured.already_running === true
19523
+ && String(safeObject(projectUpStructured.launch).launch_id || "").trim() === "launch-project-up-1",
19524
+ `routes=${ensureArray(projectUpStructured.enabled_routes_for_selection).join(",")} launch=${String(safeObject(projectUpStructured.launch).launch_id || "").trim() || "-"} already_running=${projectUpStructured.already_running ? "true" : "false"}`,
19525
+ );
19526
+ } catch (err) {
19527
+ push("local_runner_project_up_tool_dispatches", false, String(err?.message || err));
19469
19528
  }
19470
19529
 
19471
19530
  try {
@@ -629,13 +629,20 @@ export async function handleLocalProjectToolDispatch(
629
629
  ...toolArgs,
630
630
  ...(Object.prototype.hasOwnProperty.call(toolArgs, "project_id") ? {} : { project_id: resolveProjectID(toolArgs, args, workspaceDir, deps) }),
631
631
  ...(Object.prototype.hasOwnProperty.call(toolArgs, "start") ? {} : { start: false }),
632
- ...(Object.prototype.hasOwnProperty.call(toolArgs, "start_detached") ? {} : { start_detached: false }),
632
+ ...(Object.prototype.hasOwnProperty.call(toolArgs, "start_detached") ? {} : { start_detached: true }),
633
633
  ...(Object.prototype.hasOwnProperty.call(toolArgs, "json") ? {} : { json: true }),
634
634
  };
635
635
  const result = await buildRunnerProjectUpResult(normalizedFlags);
636
+ let launchPayload = null;
637
+ if (result.shouldStartRunner && result.startDetachedRequested && !result.applyFailureBlocksStart) {
638
+ launchPayload = await startDetachedRunnerWithFlags(result.startFlags, "runner.project_up");
639
+ }
636
640
  const payload = {
637
641
  ...safeObject(result.summaryPayload),
638
642
  matching_routes: ensureArray(result.matchingRoutes).map((route) => String(safeObject(route).name || "").trim()).filter(Boolean),
643
+ launch: safeObject(launchPayload?.launch),
644
+ already_running: Boolean(launchPayload?.already_running),
645
+ registry_file: String(launchPayload?.registry_file || safeObject(result.summaryPayload).registry_file || "").trim(),
639
646
  };
640
647
  const text = [
641
648
  `ok: ${payload.ok ? "true" : "false"}`,
@@ -644,6 +651,10 @@ export async function handleLocalProjectToolDispatch(
644
651
  `destination_id: ${String(payload.destination_id || "").trim() || "-"}`,
645
652
  `route_apply_requested: ${payload.route_apply_requested ? "true" : "false"}`,
646
653
  `route_apply_changed: ${payload.route_apply_changed ? "true" : "false"}`,
654
+ `start_detached_requested: ${payload.start_detached_requested ? "true" : "false"}`,
655
+ `already_running: ${payload.already_running ? "true" : "false"}`,
656
+ `launch_id: ${String(safeObject(payload.launch).launch_id || "").trim() || "-"}`,
657
+ `pid: ${String(safeObject(payload.launch).pid || "").trim() || "-"}`,
647
658
  `enabled_routes_for_selection: ${ensureArray(payload.enabled_routes_for_selection).join(", ") || "-"}`,
648
659
  `next_steps: ${ensureArray(payload.next_steps).join(" | ") || "-"}`,
649
660
  ].join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.288",
3
+ "version": "0.2.290",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [