
























@@ -0,0 +1,99 @@
1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
4+import { applyMergePatch } from "../../config/merge-patch.js";
5+import type { BundleMcpConfig, BundleMcpServerConfig } from "../../plugins/bundle-mcp.js";
6+import {
7+applyCommonServerConfig,
8+decodeHeaderEnvPlaceholder,
9+isRecord,
10+normalizeStringRecord,
11+} from "./bundle-mcp-adapter-shared.js";
12+13+async function readJsonObject(filePath: string): Promise<Record<string, unknown>> {
14+try {
15+const raw = JSON.parse(await fs.readFile(filePath, "utf-8")) as unknown;
16+return raw && typeof raw === "object" && !Array.isArray(raw)
17+ ? ({ ...raw } as Record<string, unknown>)
18+ : {};
19+} catch {
20+return {};
21+}
22+}
23+24+function resolveEnvPlaceholder(
25+value: string,
26+inheritedEnv: Record<string, string> | undefined,
27+): string {
28+const decoded = decodeHeaderEnvPlaceholder(value);
29+if (!decoded) {
30+return value;
31+}
32+const resolved = inheritedEnv?.[decoded.envVar] ?? process.env[decoded.envVar] ?? "";
33+return decoded.bearer ? `Bearer ${resolved}` : resolved;
34+}
35+36+function normalizeGeminiServerConfig(
37+server: BundleMcpServerConfig,
38+inheritedEnv: Record<string, string> | undefined,
39+): Record<string, unknown> {
40+const next: Record<string, unknown> = {};
41+applyCommonServerConfig(next, server);
42+if (typeof server.type === "string") {
43+next.type = server.type;
44+}
45+const headers = normalizeStringRecord(server.headers);
46+if (headers) {
47+next.headers = Object.fromEntries(
48+Object.entries(headers).map(([name, value]) => [
49+name,
50+resolveEnvPlaceholder(value, inheritedEnv),
51+]),
52+);
53+}
54+if (typeof server.trust === "boolean") {
55+next.trust = server.trust;
56+}
57+return next;
58+}
59+60+export async function writeGeminiSystemSettings(
61+mergedConfig: BundleMcpConfig,
62+inheritedEnv: Record<string, string> | undefined,
63+): Promise<{ env: Record<string, string>; cleanup: () => Promise<void> }> {
64+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gemini-mcp-"));
65+const settingsPath = path.join(tempDir, "settings.json");
66+const existingSettingsPath =
67+inheritedEnv?.GEMINI_CLI_SYSTEM_SETTINGS_PATH ?? process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH;
68+const base =
69+typeof existingSettingsPath === "string" && existingSettingsPath.trim()
70+ ? await readJsonObject(existingSettingsPath)
71+ : {};
72+const normalizedConfig: BundleMcpConfig = {
73+mcpServers: Object.fromEntries(
74+Object.entries(mergedConfig.mcpServers).map(([name, server]) => [
75+name,
76+normalizeGeminiServerConfig(server, inheritedEnv),
77+]),
78+) as BundleMcpConfig["mcpServers"],
79+};
80+const settings = applyMergePatch(base, {
81+mcp: {
82+allowed: Object.keys(normalizedConfig.mcpServers),
83+},
84+mcpServers: normalizedConfig.mcpServers,
85+}) as Record<string, unknown>;
86+if (!isRecord(settings.mcp) || !isRecord(settings.mcpServers)) {
87+throw new Error("Gemini MCP settings merge produced an invalid object");
88+}
89+await fs.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
90+return {
91+env: {
92+ ...inheritedEnv,
93+GEMINI_CLI_SYSTEM_SETTINGS_PATH: settingsPath,
94+},
95+cleanup: async () => {
96+await fs.rm(tempDir, { recursive: true, force: true });
97+},
98+};
99+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。