@algosuite/vo-mcp 0.2.0-beta.7 → 0.2.0-beta.9

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
@@ -2,10 +2,34 @@
2
2
 
3
3
  Virtual Office MCP server — the open protocol surface that exposes VO's consensus and ratchet tool family to any MCP-capable LLM client (Claude Code, Claude Desktop, Cursor, Continue, Codex, etc.).
4
4
 
5
- **Status:** Phase 2. Stdio transport. **20 tools registered** (source of truth: `src/server.ts` `buildToolRegistry()`). The static-ratchet tools (`vo_check_assertion_strength`, `vo_check_ratchets`) run locally today; the consensus-routed tools fall back to `{ verdict: "unimplemented", ... }` until engine credentials are present; the heal-family, PR-admin, and session-state tools forward to the vo-control-plane admin proxy in cloud mode. `vo_review_merge` is a **read-only consensus pre-merge review** (the verify-before-act gate for the Command Center — never merges). The shape is stable; the institutional-knowledge content is loaded from closed packages.
5
+ The live AlgoHQ whiteboard is available to every MCP client through
6
+ `hq_whiteboard_post` and `hq_whiteboard_read`. Both use the scoped credential
7
+ created by `vo-mcp login`; tenant/operator ownership is derived by the control
8
+ plane and cannot be widened by tool input. The control plane admits scoped
9
+ credentials only for operator IDs on its internal `HQ_WHITEBOARD_OPERATOR_IDS`
10
+ allowlist; arbitrary self-serve tenants cannot read or write the shared fleet board.
11
+
12
+ **Status:** Phase 2. Stdio transport. **26 tools registered** (source of truth: `src/server.ts` `buildToolRegistry()`). The static-ratchet tools (`vo_check_assertion_strength`, `vo_check_ratchets`) run locally today; the consensus-routed tools fall back to `{ verdict: "unimplemented", ... }` until engine credentials are present; the heal-family, PR-admin, session-state, and AlgoHQ whiteboard tools forward to the control plane in cloud mode. `vo_review_merge` is a **read-only consensus pre-merge review** (the verify-before-act gate for the Command Center — never merges). The shape is stable; the institutional-knowledge content is loaded from closed packages.
6
13
 
7
14
  This package is intentionally **shell-only**. Per `docs/handoffs/vo-mcp-server-2026-05-21.md` §C-1: tool *definitions* (schemas, names, descriptions) ship openly. Tool *implementations* that encode institutional knowledge (specific ratchet thresholds, consensus prompt content, architectural-defaults knowledge base) live behind the cloud or in closed companion packages.
8
15
 
16
+ ## Install and refresh client configuration
17
+
18
+ `vo-mcp install` safely registers the same required `algohq` MCP server in
19
+ Claude Desktop, Claude Code, and Codex (`~/.codex/config.toml`), then offers
20
+ pairing and runner auto-start. Existing client settings and comments are
21
+ preserved, and every changed config is backed up first.
22
+
23
+ To refresh client configuration on an already-paired runner without touching
24
+ its keychain credential or auto-start service, use the noninteractive form:
25
+
26
+ ```sh
27
+ vo-mcp install --config-only
28
+ ```
29
+
30
+ Restart the MCP clients and runner after updating, then use
31
+ [AlgoHQ](https://algosuite.ai/algohq) to dispatch work.
32
+
9
33
  ## Tools
10
34
 
11
35
  | Name | Phase 1 status | Description |
@@ -44,7 +68,7 @@ pnpm test # vitest run
44
68
 
45
69
  The integration test (`test/integration/stdio-roundtrip.test.ts`) spawns the built CLI, exchanges JSON-RPC over stdio, and asserts:
46
70
 
47
- - `initialize` + `tools/list` returns the 20 expected tools.
71
+ - `initialize` + `tools/list` returns the 26 expected tools.
48
72
  - `vo_check_assertion_strength` runs end-to-end and writes a JSONL event line.
49
73
  - A repeat call with identical input hits the cache (`cache.hit = true`).
50
74
  - `vo_consensus_judgment` returns `unimplemented` and still writes its event line.
package/bin/vo-mcp CHANGED
@@ -5,10 +5,11 @@
5
5
  * Usage:
6
6
  * vo-mcp # MCP stdio server (default)
7
7
  * vo-mcp install # one-command installer
8
+ * vo-mcp install --config-only # refresh Claude + Codex MCP config only
8
9
  * vo-mcp update # update MCP + runner package
9
10
  * vo-mcp login # credential login (browser loopback)
10
11
  * vo-mcp pair # device-code pairing (enter a code in the web)
11
- * vo-mcp runner # agent runner daemon
12
+ * vo-mcp runner # supervised agent runner
12
13
  * vo-mcp runner --install-autostart # register runner to start at login
13
14
  * vo-mcp runner --uninstall-autostart # remove auto-start registration
14
15
  */
@@ -31,9 +32,11 @@ if (command === 'install') {
31
32
  import('../dist/autostart-cli.js').then((m) => m.installAutostartCli());
32
33
  } else if (subcommand === '--uninstall-autostart') {
33
34
  import('../dist/autostart-cli.js').then((m) => m.uninstallAutostartCli());
34
- } else {
35
- // Bring-your-own runner daemon (bundled by scripts/bundle.mjs into dist/).
35
+ } else if (process.argv.includes('--once') || process.argv.includes('--status') || process.argv.includes('--version') || process.argv.includes('-v')) {
36
+ // Direct diagnostic/one-shot mode; remote maintenance uses the supervisor.
36
37
  import('../dist/runner-cli.js');
38
+ } else {
39
+ import('../dist/runner-supervisor.js');
37
40
  }
38
41
  } else {
39
42
  // Default: MCP stdio server
package/dist/cli.js CHANGED
@@ -5592,6 +5592,149 @@ async function handlePrivateKnowledgeContext(_deps, rawInput, _signal, fetchFn =
5592
5592
  return jsonContent({ tool: CONTEXT_TOOL_NAME, schema_version: 1, payload });
5593
5593
  }
5594
5594
 
5595
+ // src/tools/hq/whiteboard.ts
5596
+ init_auth_token_source();
5597
+ init_credential_store();
5598
+ init_common();
5599
+ var POST_TOOL_NAME = "hq_whiteboard_post";
5600
+ var READ_TOOL_NAME = "hq_whiteboard_read";
5601
+ var postDescription = "Post an append-only coordination note to the live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login; operator and tenant ownership are derived by the server.";
5602
+ var readDescription = "Read recent coordination notes from the caller's live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login and cannot widen tenant scope.";
5603
+ var postInputSchema = {
5604
+ type: "object",
5605
+ properties: {
5606
+ from: { type: "string", minLength: 1, maxLength: 100, description: "Agent/session display name." },
5607
+ type: { type: "string", minLength: 1, maxLength: 64, description: "Message kind, such as intent, worklog, blocker, or completion." },
5608
+ content: { type: "string", minLength: 1, maxLength: 500, description: "Short coordination note." },
5609
+ targetAgent: { type: "string", maxLength: 100 },
5610
+ tester: { type: "string", maxLength: 100 },
5611
+ tier: { type: "string", maxLength: 32 }
5612
+ },
5613
+ required: ["from", "type", "content"],
5614
+ additionalProperties: false
5615
+ };
5616
+ var readInputSchema = {
5617
+ type: "object",
5618
+ properties: {
5619
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 25 },
5620
+ since: { type: "string", description: "Optional ISO-8601 lower bound." },
5621
+ type: { type: "string", minLength: 1, maxLength: 64 }
5622
+ },
5623
+ additionalProperties: false
5624
+ };
5625
+ function resolveTimeoutMs() {
5626
+ const parsed = Number(process.env["HQ_WHITEBOARD_TIMEOUT_MS"]);
5627
+ return Number.isFinite(parsed) && parsed >= 10 && parsed <= 12e4 ? parsed : 1e4;
5628
+ }
5629
+ function isRecord(value) {
5630
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5631
+ }
5632
+ function onlyKeys(value, allowed) {
5633
+ return Object.keys(value).every((key) => allowed.includes(key));
5634
+ }
5635
+ function isBoundedString(value, min, max) {
5636
+ return typeof value === "string" && value.trim().length >= min && value.trim().length <= max;
5637
+ }
5638
+ function parsePostInput(value) {
5639
+ if (!isRecord(value) || !onlyKeys(value, ["from", "type", "content", "targetAgent", "tester", "tier"])) return null;
5640
+ if (!isBoundedString(value["from"], 1, 100)) return null;
5641
+ if (!isBoundedString(value["type"], 1, 64) || !/^[a-zA-Z0-9_-]+$/.test(value["type"].trim())) return null;
5642
+ if (!isBoundedString(value["content"], 1, 500)) return null;
5643
+ for (const [key, max] of [["targetAgent", 100], ["tester", 100], ["tier", 32]]) {
5644
+ if (value[key] !== void 0 && !isBoundedString(value[key], 0, max)) return null;
5645
+ }
5646
+ return {
5647
+ from: value["from"].trim(),
5648
+ type: value["type"].trim(),
5649
+ content: value["content"].trim(),
5650
+ ...typeof value["targetAgent"] === "string" ? { targetAgent: value["targetAgent"].trim() } : {},
5651
+ ...typeof value["tester"] === "string" ? { tester: value["tester"].trim() } : {},
5652
+ ...typeof value["tier"] === "string" ? { tier: value["tier"].trim() } : {}
5653
+ };
5654
+ }
5655
+ function parseReadInput(value) {
5656
+ if (!isRecord(value) || !onlyKeys(value, ["limit", "since", "type"])) return null;
5657
+ if (value["limit"] !== void 0 && (!Number.isInteger(value["limit"]) || Number(value["limit"]) < 1 || Number(value["limit"]) > 100)) return null;
5658
+ if (value["since"] !== void 0 && (typeof value["since"] !== "string" || Number.isNaN(Date.parse(value["since"])))) return null;
5659
+ if (value["type"] !== void 0 && !isBoundedString(value["type"], 1, 64)) return null;
5660
+ return {
5661
+ ...typeof value["limit"] === "number" ? { limit: value["limit"] } : {},
5662
+ ...typeof value["since"] === "string" ? { since: value["since"] } : {},
5663
+ ...typeof value["type"] === "string" ? { type: value["type"].trim() } : {}
5664
+ };
5665
+ }
5666
+ async function resolveCloud(fetchFn) {
5667
+ const url = process.env["VO_CONTROL_PLANE_URL"]?.trim().replace(/\/$/, "");
5668
+ if (!url) return null;
5669
+ try {
5670
+ const source = createAuthTokenSourceFromEnv(process.env, fetchFn, () => readStoredCredential(process.env));
5671
+ const token = await source?.getToken();
5672
+ return token ? { url, token } : null;
5673
+ } catch {
5674
+ return null;
5675
+ }
5676
+ }
5677
+ async function callWhiteboard(method, bodyOrQuery, signal, fetchFn = fetch) {
5678
+ const cloud = await resolveCloud(fetchFn);
5679
+ if (!cloud) {
5680
+ return {
5681
+ ok: false,
5682
+ error: "hq_whiteboard_not_configured",
5683
+ message: "Set VO_CONTROL_PLANE_URL and run vo-mcp login to install a scoped HQ credential."
5684
+ };
5685
+ }
5686
+ const timeoutSignal = AbortSignal.timeout(resolveTimeoutMs());
5687
+ const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
5688
+ const query = new URLSearchParams();
5689
+ if (method === "GET") {
5690
+ const input = bodyOrQuery;
5691
+ query.set("limit", String(input.limit ?? 25));
5692
+ if (input.since) query.set("since", input.since);
5693
+ if (input.type) query.set("type", input.type);
5694
+ }
5695
+ try {
5696
+ const response = await fetchFn(
5697
+ `${cloud.url}/api/v1/hq/whiteboard/messages${query.size ? `?${query}` : ""}`,
5698
+ {
5699
+ method,
5700
+ headers: {
5701
+ Authorization: `Bearer ${cloud.token}`,
5702
+ ...method === "POST" ? { "Content-Type": "application/json" } : {}
5703
+ },
5704
+ ...method === "POST" ? { body: JSON.stringify(bodyOrQuery) } : {},
5705
+ signal: requestSignal
5706
+ }
5707
+ );
5708
+ const text = await response.text();
5709
+ let payload;
5710
+ try {
5711
+ payload = JSON.parse(text);
5712
+ } catch {
5713
+ payload = { ok: false, error: "invalid_response", message: text.slice(0, 200) };
5714
+ }
5715
+ if (!response.ok) {
5716
+ return { ok: false, error: "hq_whiteboard_http_error", status: response.status, response: payload };
5717
+ }
5718
+ return payload;
5719
+ } catch (error) {
5720
+ return {
5721
+ ok: false,
5722
+ error: signal?.aborted ? "cancelled" : timeoutSignal.aborted ? "hq_whiteboard_timeout" : "hq_whiteboard_unreachable",
5723
+ message: error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)
5724
+ };
5725
+ }
5726
+ }
5727
+ async function handleHqWhiteboardPost(_deps, rawInput, signal) {
5728
+ const input = parsePostInput(rawInput);
5729
+ if (!input) throw invalidParams(POST_TOOL_NAME, "requires from, type, and 1-500 character content; unknown fields are rejected");
5730
+ return jsonContent(await callWhiteboard("POST", input, signal));
5731
+ }
5732
+ async function handleHqWhiteboardRead(_deps, rawInput, signal) {
5733
+ const input = parseReadInput(rawInput);
5734
+ if (!input) throw invalidParams(READ_TOOL_NAME, "limit must be 1-100, since must be ISO-8601, and unknown fields are rejected");
5735
+ return jsonContent(await callWhiteboard("GET", input, signal));
5736
+ }
5737
+
5595
5738
  // src/server.ts
5596
5739
  function buildToolRegistry() {
5597
5740
  return {
@@ -5786,6 +5929,22 @@ function buildToolRegistry() {
5786
5929
  inputSchema: contextInputSchema
5787
5930
  },
5788
5931
  handler: handlePrivateKnowledgeContext
5932
+ },
5933
+ [POST_TOOL_NAME]: {
5934
+ definition: {
5935
+ name: POST_TOOL_NAME,
5936
+ description: postDescription,
5937
+ inputSchema: postInputSchema
5938
+ },
5939
+ handler: handleHqWhiteboardPost
5940
+ },
5941
+ [READ_TOOL_NAME]: {
5942
+ definition: {
5943
+ name: READ_TOOL_NAME,
5944
+ description: readDescription,
5945
+ inputSchema: readInputSchema
5946
+ },
5947
+ handler: handleHqWhiteboardRead
5789
5948
  }
5790
5949
  };
5791
5950
  }
@@ -6280,6 +6439,9 @@ function createClaimClassifier(model, options = {}) {
6280
6439
  };
6281
6440
  }
6282
6441
 
6442
+ // src/constants/consensus-models.ts
6443
+ var XAI_CONSENSUS_MODEL = "grok-4.5";
6444
+
6283
6445
  // src/consensus/engine-client.ts
6284
6446
  function raceAbort(signal) {
6285
6447
  return new Promise((_, reject) => {
@@ -6430,6 +6592,7 @@ var DEFAULT_MODELS = {
6430
6592
  // Flash is also ~10x cheaper. 2026-06-02.
6431
6593
  google: "gemini-2.5-flash",
6432
6594
  deepseek: "deepseek-chat",
6595
+ xai: XAI_CONSENSUS_MODEL,
6433
6596
  meta: META_CONSENSUS_MODEL
6434
6597
  };
6435
6598
  function probeProviders(env = process.env) {
@@ -6438,6 +6601,7 @@ function probeProviders(env = process.env) {
6438
6601
  if ((env["OPENAI_API_KEY"] ?? "").trim().length > 0) out.push("openai");
6439
6602
  if ((env["GOOGLE_API_KEY"] ?? "").trim().length > 0) out.push("google");
6440
6603
  if ((env["DEEPSEEK_API_KEY"] ?? "").trim().length > 0) out.push("deepseek");
6604
+ if ((env["XAI_API_KEY"] ?? "").trim().length > 0) out.push("xai");
6441
6605
  if ((env[META_MODEL_API_KEY_ENV] ?? "").trim().length > 0 || (env[META_MODEL_API_KEY_ALIAS] ?? "").trim().length > 0) out.push("meta");
6442
6606
  return out;
6443
6607
  }
@@ -6461,7 +6625,7 @@ async function loadFactoryAndCallers(injectedEngine, injectedShared) {
6461
6625
  return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };
6462
6626
  }
6463
6627
  }
6464
- if (sharedMod === null || typeof sharedMod !== "object" || typeof sharedMod.callAnthropicWithMetrics !== "function" || typeof sharedMod.callOpenAIWithMetrics !== "function" || typeof sharedMod.callGeminiWithMetrics !== "function") {
6628
+ if (sharedMod === null || typeof sharedMod !== "object" || typeof sharedMod.callAnthropicWithMetrics !== "function" || typeof sharedMod.callOpenAIWithMetrics !== "function" || typeof sharedMod.callGeminiWithMetrics !== "function" || typeof sharedMod.callXAIWithMetrics !== "function") {
6465
6629
  return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };
6466
6630
  }
6467
6631
  return {
@@ -6485,6 +6649,7 @@ async function tryCreateEngineConsensusClientFromEnvAsync(options = {}) {
6485
6649
  openai: loaded.shared.callOpenAIWithMetrics,
6486
6650
  google: loaded.shared.callGeminiWithMetrics,
6487
6651
  deepseek: loaded.shared.callDeepSeekWithMetrics,
6652
+ xai: loaded.shared.callXAIWithMetrics,
6488
6653
  meta: options.metaCaller ?? callMetaWithMetrics
6489
6654
  };
6490
6655
  const modelByProvider = {
@@ -6492,6 +6657,7 @@ async function tryCreateEngineConsensusClientFromEnvAsync(options = {}) {
6492
6657
  openai: options.models?.openai ?? DEFAULT_MODELS.openai,
6493
6658
  google: options.models?.google ?? DEFAULT_MODELS.google,
6494
6659
  deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek,
6660
+ xai: options.models?.xai ?? DEFAULT_MODELS.xai,
6495
6661
  meta: options.models?.meta ?? DEFAULT_MODELS.meta
6496
6662
  };
6497
6663
  const adapterEnv = !(env[META_MODEL_API_KEY_ENV] ?? "").trim() && (env[META_MODEL_API_KEY_ALIAS] ?? "").trim() ? { ...env, [META_MODEL_API_KEY_ENV]: env[META_MODEL_API_KEY_ALIAS] } : env;
@@ -6658,6 +6824,69 @@ function tryCreateMoatConsensusClientFromEnv(env = process.env, fetchFn) {
6658
6824
  });
6659
6825
  }
6660
6826
 
6827
+ // src/consensus/fallback-client.ts
6828
+ var MIN_VALID_LOCAL_VERDICTS = 2;
6829
+ var INSUFFICIENT_LOCAL_VERDICTS_REASON = "local-panel-insufficient-valid-verdicts";
6830
+ function createConsensusFallbackClient(primary, fallback, options = {}) {
6831
+ return {
6832
+ async run(request) {
6833
+ const primaryResult = await primary.run(request);
6834
+ if (request.signal?.aborted || !primaryResult.ok && primaryResult.reason === CANCELLED_REASON) {
6835
+ return primaryResult;
6836
+ }
6837
+ if (primaryResult.ok) {
6838
+ const validVerdicts = primaryResult.per_model_verdicts.filter(
6839
+ (verdict) => verdict.verdict !== "error"
6840
+ );
6841
+ if (validVerdicts.length >= MIN_VALID_LOCAL_VERDICTS) return primaryResult;
6842
+ options.onFallback?.(INSUFFICIENT_LOCAL_VERDICTS_REASON);
6843
+ return fallback.run(request);
6844
+ }
6845
+ options.onFallback?.(primaryResult.reason);
6846
+ return fallback.run(request);
6847
+ }
6848
+ };
6849
+ }
6850
+
6851
+ // src/consensus/local-credential-env.ts
6852
+ import { createRequire as createRequire2 } from "node:module";
6853
+ var require2 = createRequire2(import.meta.url);
6854
+ var KEY_SERVICE = "algosuite-vo";
6855
+ var KEYCHAIN_TARGETS = [
6856
+ { account: "anthropic-api-key", envVar: "ANTHROPIC_API_KEY" },
6857
+ { account: "openai-api-key", envVar: "OPENAI_API_KEY" },
6858
+ { account: "xai-api-key", envVar: "XAI_API_KEY" },
6859
+ { account: "meta-api-key", envVar: "MODEL_API_KEY" }
6860
+ ];
6861
+ function loadEntryCtor() {
6862
+ try {
6863
+ return require2("@napi-rs/keyring").Entry ?? null;
6864
+ } catch {
6865
+ return null;
6866
+ }
6867
+ }
6868
+ function readKey(EntryCtor, account) {
6869
+ try {
6870
+ return new EntryCtor(KEY_SERVICE, account).getPassword()?.trim() || null;
6871
+ } catch {
6872
+ return null;
6873
+ }
6874
+ }
6875
+ function withLocalConsensusCredentials(baseEnv = process.env, options = {}) {
6876
+ const env = { ...baseEnv };
6877
+ if (!env.OPENAI_API_KEY?.trim() && env.CODEX_API_KEY?.trim()) {
6878
+ env.OPENAI_API_KEY = env.CODEX_API_KEY;
6879
+ }
6880
+ const EntryCtor = options.EntryCtor === void 0 ? loadEntryCtor() : options.EntryCtor;
6881
+ if (!EntryCtor) return env;
6882
+ for (const target of KEYCHAIN_TARGETS) {
6883
+ if (env[target.envVar]?.trim()) continue;
6884
+ const key = readKey(EntryCtor, target.account);
6885
+ if (key) env[target.envVar] = key;
6886
+ }
6887
+ return env;
6888
+ }
6889
+
6661
6890
  // src/cloud/login.ts
6662
6891
  init_credential_store();
6663
6892
  import { createServer as createServer2 } from "node:http";
@@ -6921,11 +7150,23 @@ async function main() {
6921
7150
  const ratchets = createStubRatchetClient();
6922
7151
  const testModule = process.env["VO_MCP_TEST_ENGINE_MODULE"];
6923
7152
  const testClient = testModule !== void 0 && testModule.length > 0 ? await loadTestEngineClient(testModule) : null;
7153
+ const localEnv = withLocalConsensusCredentials();
7154
+ const localProviders = probeProviders(localEnv);
7155
+ for (const key of ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY", "DEEPSEEK_API_KEY", "XAI_API_KEY", "MODEL_API_KEY"]) {
7156
+ if (!process.env[key] && localEnv[key]) process.env[key] = localEnv[key];
7157
+ }
7158
+ const localConsensus = tryCreateEngineConsensusClientFromEnv({ envSource: localEnv });
6924
7159
  const cloudConsensus = testClient ? null : tryCreateMoatConsensusClientFromEnv();
6925
- if (cloudConsensus) {
6926
- console.error("[vo-mcp] cloud consensus active \u2014 verdicts via vo-moat-plane (no local model keys)");
7160
+ let consensus = testClient ?? localConsensus;
7161
+ if (!testClient && cloudConsensus && localProviders.length >= 2) {
7162
+ console.error(`[vo-mcp] local-first consensus active (${localProviders.join(", ")}); cloud moat is fallback-only`);
7163
+ consensus = createConsensusFallbackClient(localConsensus, cloudConsensus, {
7164
+ onFallback: (reason) => console.error(`[vo-mcp] local consensus unavailable (${reason}); using cloud moat fallback`)
7165
+ });
7166
+ } else if (!testClient && cloudConsensus) {
7167
+ console.error("[vo-mcp] fewer than 2 linked local providers; cloud moat consensus active");
7168
+ consensus = cloudConsensus;
6927
7169
  }
6928
- const consensus = testClient ?? cloudConsensus ?? tryCreateEngineConsensusClientFromEnv();
6929
7170
  let adminCallables = null;
6930
7171
  try {
6931
7172
  adminCallables = buildAdminCallableClientFromEnv();